Open kindaro opened 3 years ago
An example that declaratively creates a «close» button:
module Main where
import qualified Control.Concurrent.Async as Async
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative
import GI.Gtk.Declarative.State
import GI.Gtk.Declarative.EventSource
main ∷ IO ( )
main = do
_ ← Gtk.init Nothing
main ← Async.async Gtk.main
let windowWidget = bin Gtk.Window [#title := ""] (widget Gtk.Button [#label := "close", on #clicked ( )])
windowState ← create @Widget windowWidget
window ← someStateWidget windowState
#showAll window
subscribe windowWidget windowState (\ ( ) → Gtk.mainQuit)
Async.wait main
I don't really have a general answer to this question... but personally I ended up forking gi-gtk-declarative and hacking a component system in - see: https://github.com/Dretch/gi-gtk-declarative
I'd like to move the component system to a package that sits on-top of the regular gi-gtk-declarative, but so far I can't find a way around the Functor
constraint on widgets (since this means you can't do dynamic typing with events, basically).
I don't have any clean example of another architecture to share, unfortunately. https://github.com/owickstrom/komposition uses this library but with an indexed monad approach to stateful resources (GTK windows primarily). It's a bunch of (some mutually recursive) functions that create new windows and modals, patch the widget trees in them, and eventually destroys the resources they created. It's more advanced and I believe hard to generalize, but I would make sense with a simplified example. I haven't had time to extract it from Komposition, though. If anyone would like to have a stab it I think it would be a welcome contribution to the examples!
By the way, there's an in-between approach that comes to mind, where you don't go all the way to indexed monads to ensure correct resource handling. You could just have values that represent windows/modals, and define create/update/destroy functions for such resources. I wrote about that style in Finite-State Machines, Part 2: Explicit Typed State Transitions.
With Linear Haskell that could even be made safe, but it's not something I've fiddled with.
I appreciate the experts sharing their advanced architectures with me and I would like to eventually explore them all. But at this time it would be hard for me to do so. I have not been working with GUI previously, so for me the question is more like «how to build up from first principles», rather than «how to get more power». I hardly even understand the basics, so it would be hard for me to appreciate the benefits of a finer architecture.
Observe that any underlying imperative platform may be wrapped into the «update + view + state₀» idiom:
Almost the same! So, while gi-gtk-declarative-app-simple
is a comfortable interface, it does not give me a way to appreciate the features of gi-gtk-declarative
per se.
gi-gtk-declarative-app-simple
is not at all trivial, and I have not been able to reverse engineer it yet. (At this time I am trying to get both a subscription and an event in the same place by smuggling the event from subscribe
through an MVar
and I get a thread blocked indefinitely
exception.) Some of this complexity is essential, some is accidental, and some of it is due to the interface offered by gi-gtk-declarative
being hard to use.
For example, patch
wants two widgets and a SomeState
, and it is implied that the SomeState
was obtained from the first widget — but what if I confuse the widgets? Since SomeState
is made from a widget, it may as well remember from which. Then patch
would only take SomeState
and a new widget. subscribe
is another example — it provides an event and a Subscription
in two different places, and then I have to somehow cancel the subscription while handling the event. Also, the same logic applies as to patch
— if the widget was bundled into SomeState
, subscribe
would need one argument less.
This is why I think it would be good to give an example of how the basic building blocks offered by gi-gtk-declarative
may be put together in the most simple way. On the one hand, it will give the reader an appreciation of how gi-gtk-declarative
works, and on the other hand it may show us ways to improve and simplify its interface.
P. S. I fixed the MVar
error. But now I get a transient segmentation fault. Eh.
There are many great examples, but all of them use
App.Simple
. I would like to use some other architecture for my application. A simple example of opening a window with some buttons would go a long way. So far I have this:It works so far, but it took me something like 2 hours to get there! And it does not really use any features of
gi-gtk-declarative
because I could not figure out how to open a window yet.