forked from HeinrichApfelmus/threepenny-gui
-
Notifications
You must be signed in to change notification settings - Fork 3
/
CRUD.hs
138 lines (112 loc) · 5.18 KB
/
CRUD.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{-----------------------------------------------------------------------------
threepenny-gui
Example:
Small database with CRUD operations and filtering.
To keep things simple, the list box is rebuild every time
that the database is updated. This is perfectly fine for rapid prototyping.
A more sophisticated approach would use incremental updates.
------------------------------------------------------------------------------}
{-# LANGUAGE RecursiveDo #-}
import Prelude hiding (lookup)
import Control.Monad (void)
import Data.List (isPrefixOf)
import Data.Maybe
import Data.Monoid
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (delete)
{-----------------------------------------------------------------------------
Main
------------------------------------------------------------------------------}
main :: IO ()
main = startGUI defaultConfig setup
setup :: Window -> UI ()
setup window = void $ mdo
return window # set title "CRUD Example (Simple)"
-- GUI elements
createBtn <- UI.button #+ [string "Create"]
deleteBtn <- UI.button #+ [string "Delete"]
listBox <- UI.listBox bListBoxItems bSelection bDisplayDataItem
filterEntry <- UI.entry bFilterString
((firstname, lastname), tDataItem)
<- dataItem bSelectionDataItem
-- GUI layout
element listBox # set (attr "size") "10" # set style [("width","200px")]
let uiDataItem = grid [[string "First Name:", element firstname]
,[string "Last Name:" , element lastname]]
let glue = string " "
getBody window #+ [grid
[[row [string "Filter prefix:", element filterEntry], glue]
,[element listBox, uiDataItem]
,[row [element createBtn, element deleteBtn], glue]
]]
-- events and behaviors
bFilterString <- stepper "" . rumors $ UI.userText filterEntry
let tFilter = isPrefixOf <$> UI.userText filterEntry
bFilter = facts tFilter
eFilter = rumors tFilter
let eSelection = rumors $ UI.userSelection listBox
eDataItemIn = rumors $ tDataItem
eCreate = UI.click createBtn
eDelete = UI.click deleteBtn
-- database
-- bDatabase :: Behavior (Database DataItem)
let update' mkey x = flip update x <$> mkey
bDatabase <- accumB emptydb $ concatenate <$> unions
[ create ("Emil","Example") <$ eCreate
, filterJust $ update' <$> bSelection <@> eDataItemIn
, delete <$> filterJust (bSelection <@ eDelete)
]
-- selection
-- bSelection :: Behavior (Maybe DatabaseKey)
bSelection <- stepper Nothing $ head <$> unions
[ eSelection
, Nothing <$ eDelete
, Just . nextKey <$> bDatabase <@ eCreate
, (\b s p -> b >>= \a -> if p (s a) then Just a else Nothing)
<$> bSelection <*> bShowDataItem <@> eFilter
]
let bLookup :: Behavior (DatabaseKey -> Maybe DataItem)
bLookup = flip lookup <$> bDatabase
bShowDataItem :: Behavior (DatabaseKey -> String)
bShowDataItem = (maybe "" showDataItem .) <$> bLookup
bDisplayDataItem = (UI.string .) <$> bShowDataItem
bListBoxItems :: Behavior [DatabaseKey]
bListBoxItems = (\p show -> filter (p. show) . keys)
<$> bFilter <*> bShowDataItem <*> bDatabase
bSelectionDataItem :: Behavior (Maybe DataItem)
bSelectionDataItem = (=<<) <$> bLookup <*> bSelection
-- automatically enable / disable editing
let
bDisplayItem :: Behavior Bool
bDisplayItem = maybe False (const True) <$> bSelection
element deleteBtn # sink UI.enabled bDisplayItem
element firstname # sink UI.enabled bDisplayItem
element lastname # sink UI.enabled bDisplayItem
{-----------------------------------------------------------------------------
Database Model
------------------------------------------------------------------------------}
type DatabaseKey = Int
data Database a = Database { nextKey :: !Int, db :: Map.Map DatabaseKey a }
emptydb = Database 0 Map.empty
keys = Map.keys . db
create x (Database newkey db) = Database (newkey+1) $ Map.insert newkey x db
update key x (Database newkey db) = Database newkey $ Map.insert key x db
delete key (Database newkey db) = Database newkey $ Map.delete key db
lookup key (Database _ db) = Map.lookup key db
{-----------------------------------------------------------------------------
Data items that are stored in the data base
------------------------------------------------------------------------------}
type DataItem = (String, String)
showDataItem (firstname, lastname) = lastname ++ ", " ++ firstname
-- | Data item widget, consisting of two text entries
dataItem
:: Behavior (Maybe DataItem)
-> UI ((Element, Element), Tidings DataItem)
dataItem bItem = do
entry1 <- UI.entry $ fst . maybe ("","") id <$> bItem
entry2 <- UI.entry $ snd . maybe ("","") id <$> bItem
return ( (getElement entry1, getElement entry2)
, (,) <$> UI.userText entry1 <*> UI.userText entry2
)