问题
What I want to do is set up fields which show detail when they're in focus, but summary when they're not. eg.
a). when it loses focus (gets blur?), I save the value in a (State?) Map and then change the value to a function of the old value (ie. the summary value)
b). when it gets focus - I replace the summary value with the old value that I saved in the Map
I cant' figure out how to do this, but I think I probably need a state monad and the UI monad. My try is:
renderField :: Map->Int->UI (Element, Map)
renderField vs ix = do
input <- UI.input
on UI.blur input $ \_ -> void $ do
fieldValue <- get value input
let newVs = insert ix fieldValue vs
return input # set UI.value (calcNewValue fieldValue)
on UI.focus input $ \_ -> void $ do
let savedValue = findWithDefault "" ix vs
return input # set UI.value savedValue
return (input, newVs)
but I can't get this map to work - because it needs to track all the calls.... I guess it should be State monad or something?
Thanks.
N
回答1:
Indeed, you need to keep track of state.
However, the usual pattern s -> (a,s)
(the state monad) does not apply here, as you are working with callback functions. For these, you need a different pattern.
In the traditional imperative style, one would use a mutable variable here, for instance an IORef
. Note that it is no longer necessary to keep track of indices -- you can think of an IORef
as being an index in a large mutable map.
renderField :: UI Element
renderField = do
input <- UI.input
state <- liftIO $ newIORef
on UI.blur input $ \_ -> do
fieldValue <- get value input
liftIO $ writeIORef state fieldValue
element input # set UI.value (calcNewValue fieldValue)
on UI.focus input $ \_ -> do
savedValue <- liftIO $ readIORef state
element input # set UI.value savedValue
return input
Alternatively, you can also use functional reactive programming (FRP) in Threepenny. Note that the API is still somewhat preliminary, the following code is specific to threepenny-gui
version 0.4.*
:
renderField :: UI Element
renderField = do
input <- UI.input
bValueUser <- stepper "" $ UI.valueChange input
bState <- stepper "" $ bValueUser <@ UI.blur input
bValue <- stepper "" $ fmap head $ unions
[ (calcNewValue <$> bValueUser) <@ UI.blur input
, bState <@ UI.focus input
]
element input # sink UI.value bValue
Again, there are still a couple of subtleties and warts in this code, but this is the general direction I want to head in. Some preliminary information about FRP and how it applies to GUI development can be found in the documentation.
My recommendation is to use the familiar solution (IORef
) when you need to get something done quickly, and explore the FRP solution when you have plenty of free time. The example code mainly uses the FRP style.
(Disclosure: I'm the author of Threepenny.)
来源:https://stackoverflow.com/questions/20872825/trying-to-render-fields-in-threepenny-gui-with-special-behaviour