Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling mutually recursive GUI widgets with reactive-banana

I am hunting for a library to write a GUI on top of GLFW and OpenGL. I'm doing this because I am dissatisfied with the common UI library bindings which I feel are too imperative, and I would also like tight control of the look and feel of my UIs. I'd like a declarative approach to defining UI's. I am experimenting with reactive-banana (and temporarily reactive-banana-wx) to see if it meets my needs. I have a problem with defining recursive widgets. Here's my simplest test case:

  • A text widget that displays a counter.
  • A button widget that increments the counter.
  • A button widget that is inactive (so it is greyed out and does not respond to input at all) when the counter is 0 and otherwise active and resets the counter to 0.

The first and third widget have a recursive relationship. The first widget is intuitively a stepper of a union of events fed from the two buttons. However, the reset button is an fmap of the counter, and then the event stream relies on the reset button! What is to be done?

Beyond this question I have a concern about event handling: Since I want to handle device input and input focus within my code instead of relying on a framework, I see difficulties ahead in correctly dispatching events in a scalable way. Ideally I would define a data that encapsulates the hierarchical structure of a widget, a way to install event callbacks between the elements, and then write a function that traverses that data structure in order to define device input processing and graphical output. I am not sure how to take an event stream and split it as easily as event streams can be merged.

like image 821
danharaj Avatar asked Nov 04 '12 01:11

danharaj


1 Answers

Recursion is allowed, as long as it is mutual recursion between a Behavior and an Event. The nice thing about Behaviors is that sampling them at the time of an update will return the old value.

For instance, your example can be expressed as follows

eClick1, eClick2 :: Event t ()

bCounter :: Behavior t Int
bCounter = accumB 0 $ mconcat [eIncrement, eReset]

eIncrement = (+1)      <$ eClick1
eReset     = (const 0) <$ whenE ((> 0) <$> bCounter) eClick2

See also the question "Can reactive-banana handle cycles in the network?"


As for your second question, you seem to be looking for the function filterE and its cousins filterApply and whenE?


As for your overall goal, I think it is quite ambitious. From what little experience I have gained so far, it seems to me that binding to an existing framework feels quite different from making an "clean-state" framework in FRP. Most likely, there are still some undiscovered (but exciting!) abstractions lurking there. I once started to write an application called BlackBoard that contains a nice abstraction about time-varying drawings.

However, if you care more about the result rather than the adventure, I would recommend a conservative approach: create the GUI toolkit in an imperative style and hook reactive-banana on top of that to get the benefits of FRP.

In case you just wish for any GUI, I am currently focussing on the web browser as a GUI. Here some preliminary experiments with Ji. The main benefit over wxHaskell is that it's a lot easier to get up and running and any API design efforts will benefit a very wide audience.

like image 108
Heinrich Apfelmus Avatar answered Sep 21 '22 20:09

Heinrich Apfelmus