How do you get the content of ui elements when using reactive-banana? The event0 returns an event of type Event (), which has unit type instead of the type of the control. event1 takes an event of type Event w (a -> IO ()), but command is of type Event w (IO ()). mapAccumE and mapAccumB takes pure functions as parameters, so get text foo can't be used with them.
Basically, you want to work with functions instead of data. If you're thinking "How do I create a behavior which has the current text in a box", you don't. Instead you write functions that take the current text as a parameter, and pass it in when necessary. Suppose you want to print the contents of a textbox when a button is pressed. Then you would do something like this:
eButton :: NetworkDescription (Event ())
eButton = event0 button command
network = do
pressButton <- eButton
reactimate $ (\() -> get text foo >>= print) <$> pressButton
If you need to get input into a Behavior, you can similarly use a function with type Behavior (String -> a) (or whatever type you need), and then just pass the string in at the point of the reactimate call.
(Author of reactive-banana speaking. Sorry for the late reply, the possibility of questions being asked here didn't even cross my mind. :-) )
I discovered today that I omitted a very crucial feature from the library: getting the content of a UI element as a Behavior. Embarassing! :-D
John describes the current workaround, but the next version of reactive-banana will include the missing feature.
EDIT: I have released reactive-banana version 0.4 which now includes the functionality in form of a function
fromPoll :: IO a -> NetworkDescription (Behavior a)
Related
I find myself in desire of a function with the signature:
-- VtyWidget is from https://github.com/reflex-frp/reflex-vty
now :: a -> VtyWidget t m (Event t a)
For some m. This function would take an element and produce a widget which fires an event once, immediately.
I cannot find such a function or any things that I could use to build this.
I can build things of this type like:
const (pure never)
But that ignores the input and never fires an event.
Is there a way to do this? Or am I not supposed to do this sort of thing?
I'm not sure I fully understand the question but it vaguely sounds like you may want getPostBuild. That is what I use whenever I want things to happen once during initialization of a widget.
Using Gtk2Hs, I want to set up a callback to listen for "Owner change" events for the clipboard.
I can find out if this is supported by the X server by querying:
display <- fmap fromJust displayGetDefault
canBeNotified <- displayRequestSelectionNotification display selectionPrimary
It should be straight forward to do like this in C:
GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
g_signal_connect(clipboard, "owner-change",
G_CALLBACK(handle_owner_change), NULL);
I see that callbacks in Gtk2Hs are set up like this:
<widget> `on` <event> $ <action>
Problems
I cannot find any OwnerChangeEvent. Graphics.UI.Gtk.Gdk.EventM does only provide functions of type EventM to query the owner change events.
on from System.Glib.Signals needs an associated widget takes any object and an event associated to that object (on :: object -> Signal object callback -> callback -> IO (ConnectId object). If I have understood X correctly, it should not be necessary to have a window to listen for this event.
Either I have misunderstood how to wrap this together, or I have reached upon a yet not implemented part of gtk in Gtk2Hs.
I therefore looked at the definition of keyPressEvent which looks like this:
keyPressEvent :: WidgetClass self => Signal self (EventM EKey Bool)
keyPressEvent = Signal (eventM "key_press_event" [KeyPressMask])
So, I tested by simply adding a definition for ownerChangeEvent in gtk like keyPressEvent, but subsituted "key_press_event" for "owner_change_event".
That did not work as I got a runtime error about not finding the event. Naturally.
Any ideas on how to properly listen for "owner change" events in gtk if possible?
You're right that this is not implemented at the moment. Adding it shouldn't be too involved, and might be a fun project if you're looking to get into gtk2hs development. You'll want to add a new constructor to the Event type, together with a descriptive type alias like type EventOwnerChange = Event. You will need to extend marshalEvent to handle this new kind of event, and there may be an unmarshalEvent hanging around though I can't remember.
Once that's all done, it should be pretty easy to connect to the appropriate signal using the connect_* family of functions (which are for gtk2hs internal use only and therefore are not exposed in the API). From the documentation for the owner-change event, you will probably need to use something like Signal (connect_OBJECT__NONE "owner-change").
It may also be necessary to add to the EventMask type, though I'm not confident -- test it out and see.
I want to use a website as a working example in order to help learn Haskell. I'm trying to follow the Heist tutorial from the Snap website, and display the result of a factorial function in a web page.
I can get the example function defined "server side" without the compiler complaining, but I cannot figure out how to bind the function to a tag which I can then place into the HTML. Specifically, this part works fine (in, say, Site.hs):
factSplice :: Splice Snap
factSplice = do
input <- getParamNode
let text = T.unpack $ X.nodeText input
n = read text :: Int
return [X.TextNode $ T.pack $ show $ product [1..n]]
But the really important part - how to evaluate this function as part of a web page (such as how to bind it to a tag like < fact />) - is cryptic. The instructions say to drop:
bindSplice "fact" factSplice templateState
somewhere in the code. But this alone is not sufficient. This statement is not an expression (stuff = bindSplice...), so it is not clear how or where to put it in the code. Moreover, it is not at all clear where "templateState" is supposed to come from. It almost seems like "templateState" is supposed to be a placeholder for default values like emptyTemplateState or defaultHeistState, but these both appear to have been deprecated years ago, and the latest version of Heist (0.14) does not recognize them.
MightyByte has commented on this kind of question several times in 2011, but the answers all gloss over exactly the confusing part, i.e. how to actually get data into a web page. Can anyone help?
-- UPDATE --
Thank you very much, mightybyte! Your explanation and some cursory poking around in the source code cleared up a lot of confusion, and I was able to get the factorial example from the Snap website tutorial working. Here is my solution - I'm a complete n00b, so apologies if the explanation seems pedantic or obvious.
I more or less used the addConfig approach that mightybyte suggested, simply copying the implementation of addAuthSplices from SpliceHelpers.hs. I started with the default project via "snap init", and defined a function addMySplices in Site.hs
addMySplices :: HasHeist b => Snaplet (Heist b) -> Initializer b v ()
addMySplices h = addConfig h sc
where
sc = mempty & scInterpretedSplices .~ is
is = do
"fact" ## factSplice
This uses lenses to access the fields of the SpliceConfig neutral element mempty, so I had to add Control.Lens to the dependencies in Site.hs, as well as Data.Monoid to put mempty in scope. I also changed the type signature of the factorial splice to factSplice :: Monad n => I.Splice n, but the function is otherwise unchanged from its form in the Heist tutorial. Then I put a call to addMySplices in the application initializer, right next to addAuthSplices in Site.hs
app :: SnapletInit App App
...
addAuthSplices h auth
addMySplices h
...
which results in factSplice being bound to the tag <fact>. Dropping <fact>8</fact> into one of the default templates renders 40320 on the page, as advertised.
This question from about a year ago contains a superficially similar solution, but does not work with the latest version of Heist; the difference is that some fields were made accessible through lenses instead of directly, which is explained on the Snap project blog in the announcement of Heist 0.14. In particular, hcCompliedSplices has been completely redefined - there's even a friendly warning about it in Types.hs.
If you look at the top level Heist module, you'll see the initHeist function. As the documentation points out, this is the main initialization function. And you are supposed to pass it all of your templates and splices. A look at the type signature tells us these are all bundled together in the HeistConfig data type.
initHeist :: Monad n => HeistConfig n -> EitherT [String] IO (HeistState n)
This gives you back a HeistState, which is what you pass to renderTemplate when you want to render a template. Also, HeistConfig contains a SpliceConfig field for all your splices.
But this is all the low level interface. Heist was designed to have no dependencies on Snap. It is a template library and can work with any web framework. (Or even standalone with no web framework at all for things like generating HTML email.) If you're using Heist with Snap, you'll probably want to use the convenience stuff that we supply which handles the details of calling initHeist, renderTemplate, etc.
For a working example of this, your best bet is to look at the project template that you get when you do "snap init". You can see that code on github here. If you look in Application.hs, you'll see that there's a line including the Heist snaplet in the application data structure. Then, if you look at the bottom of Site.hs, you'll see that there's a call to heistInit. This function is defined by the heist snaplet here. The snaplet takes care of the initialization, state management, on-the-fly template reloading, etc for you.
So, to put this all together...from the Heist API described above, we see that we need to define our splices inside HeistConfig. But our application will interact with all this stuff mostly through the snaplet's API. So if we look in the heist snaplet API for something involving HeistConfig/SpliceConfig, we find two relevant functions:
heistInit' :: FilePath -> HeistConfig (Handler b b) -> SnapletInit b (Heist b)
addConfig :: Snaplet (Heist b) -> SpliceConfig (Handler b b) -> Initializer b v ()
These type signatures suggest two ways of defining your splices. You could use heistInit' instead of heistInit and pass your HeistConfig to it up front. Or you could add your splices afterwards by calling addConfig with the resulting Snaplet (Heist b).
I am currently working on a small game utilizing reactive banana and SDL. Since the goal is mostly to learn more about reactive banana and FRP, I attempted to use dynamic switching to set up a collection of game objects, but without much success so far.
In the Bartab example, the only example that uses event switching I've found, the event that ultimately triggers the creation of a new entry in the collection is acquired from outside the event network instead of using an internal event. My question is: Is this the only way to do it or is it just a special case for this example?
Are there any more examples of dynamic event switching being used in reactive banana?
From what I understand I get a Moment t (Anytime Behavior a), use execute to create an Event t (Anytime Behavior a), which is then in turn used to update the Behavior carrying the collection.
The Moment t (Anytime Behavior a) is created using trimB on a Behavior created from the triggering Event. If this triggering event originates in the event network, it doesn't compile with the error message "Could not deduce (t ~ t1)". I'm not sure what the ~ exactly means, but it obviously throws the error because the two Frameworks values (t) of the event network and this new Moment value are different.
So, long story short, I don't understand how Event Switching in reactive banana works and I'm not sure why. It should be relatively straightforward in theory.
Edit:
-- Event Network
-- ==========================
setupNetwork :: forall t. Frameworks t => (EventSource Command, EventSource ()) -> IORef SpriteMap -> GameMap -> Moment t ()
setupNetwork ((addHandlerE, _), (addHandlerT, _)) sprites map = do
-- Input Events
----------------
eInput <- fromAddHandler addHandlerE -- Player Commands
eFrame <- fromAddHandler addHandlerT -- Event on each Frame
let
[...]
eSpawnEvent :: Event t (DSCoord)
eSpawnEvent = extractCoord <$> eLeftClick
where
extractCoord (LeftClick c) = c
spawnPos :: Frameworks s => Moment s (AnyMoment Behavior DSCoord)
spawnPos = trimB $ stepper (0,0) eSpawnEvent
eSpawnPos <- execute $ (FrameworksMoment spawnPos <$ eSpawnEvent)
[...]
I simply tried to mirror newEntry / eNewEntry from the example, just using a normal event to create the new behavior. This produces the "Could not deduce (t ~ s)" error in spawnPos.
Edit2:
It works, but now the same error comes up on the line where I use execute to create the Event. "Could not deduce t ~ t1"
It appears to me that the code is essentially correct except for a minor mistake:
The compiler rightly complains that spawnPos has a (polymorphic) starting time s while the starting time of eSpawnEvent is fixed to t, which is different. The latter time is brought into scope via the forall t part in type signature of setupNetwork. In other words, t represents the starting time of the whole network.
The simple fix is to change the offending line to
spawnPos :: Frameworks t => Moment t (AnyMoment Behavior DSCoord)
I'm coding a http client as a learning project with the use of network-http package (http://hackage.haskell.org/package/HTTP-4000.2.2) .
There is a Network.Browser module which defines getBrowserState function.
Probably just a lame beginner question but how can I get BrowserState record from BrowserAction monad if Network.Browser module does not export data constructor or record's lift function ?
import Network.Browser
-- getBrowserState :: BrowserAction t (BrowserState t)
extractBS :: BrowserAction t (BrowserState t) -> BrowserState t
-- ??? implementation ???
In addition what about further handling of BrowserState fields like bsCookies, bsDebug, bsProxy etc. ? (http://hackage.haskell.org/packages/archive/HTTP/4000.2.2/doc/html/src/Network-Browser.html#BrowserState)
What you're trying to do doesn't really make sense. A BrowserAction describes an action, while a BrowserState describes the current state of the browser at some point within an action.
The only way of getting something out of a BrowserAction is to run it with the browse function.
browse :: BrowserAction conn a -> IO a
It's essentially the same as why you can't get a Something out of an IO Something, except here you have the function browse which allows you to "get stuff out of" a browser action by running it.
For example, you can make an action that extracts the current browser state after the original action and run that:
browse (action >> getBrowserState) :: IO (BrowserState conn)
Note the IO in the type here, as running the action may have side effects.
That said, I suspect that what you really want is to make the code that needs the current browser state part of an action.
browse $ do action
state <- getBrowserState
-- do stuff with the state
Note that BrowserAction has a MonadIO instance, so you can still do IO stuff by using liftIO.
browse $ do -- browsing
liftIO $ putStrLn "foo"
-- more browsing
In other words, you shouldn't think of it as getting stuff out of a BrowserAction. You should instead think of how to make the browsing-related code part of a BrowserAction which you then run with browse.