Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An example without App.Simple? #96

Open
kindaro opened this issue Feb 20, 2021 · 6 comments
Open

An example without App.Simple? #96

kindaro opened this issue Feb 20, 2021 · 6 comments

Comments

@kindaro
Copy link

kindaro commented Feb 20, 2021

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:

module Main where

import qualified Control.Concurrent.Async as Async
import qualified Data.GI.Base as Base
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative

main ∷ IO ( )
main = do
  _ ← Gtk.init Nothing
  main ← Async.async Gtk.main
  win ← Base.new Gtk.Window [#title Base.:= ""]
  #showAll win
  -- Insert code here.
  Gtk.mainQuit
  Async.wait main

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.

@kindaro
Copy link
Author

kindaro commented Feb 21, 2021

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

@Dretch
Copy link
Collaborator

Dretch commented Feb 21, 2021

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).

@owickstrom
Copy link
Owner

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!

@owickstrom
Copy link
Owner

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.

@kindaro
Copy link
Author

kindaro commented Feb 24, 2021

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.

@kindaro
Copy link
Author

kindaro commented Feb 24, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants