Helper functions to use reflex with gi-gtk.
This package provides a a reflex host and helper functions to create reactive GUI applications using gi-gtk and reflex.
reflex-gi-gtk
This package provides the necessary plumbing to write reactive GUI applications using GTK3+ (based on gi-gtk) and reflex.
While this packes should make working with gi-gtk and reflex together much easier than using them together without any kind of glue code, it doesn't provide a lot of abstraction on top of either GTK or reflex. People familiar with both gi-gtk and reflex separately should have no trouble using reflex-gi-gtk successfully to build awesome applications.
Installation
reflex-gi-gtk is a regular cabal library and can be added to your project by listing reflex-gi-gtk
in your project's build-depends
.
Quick start
To start writing your reactive GUI application using reflex-gi-gtk, you will farst want to start the reactive part of your application using runReflexGtk
, something like this:
{-# LANGUAGE OverloadedStrings, OverloadedLabels, FlexibleContexts #-}
import Control.Applicative (liftA2)
import GI.Gtk hiding (main)
import Reflex.GI.Gtk
import System.Environment
import System.Exit
main :: IO ()
main = do
argv <- (:) <$> getProgName <*> getArgs
Just gtkApplication <- applicationNew (Just "org.example.MyAwesomeApp") []
rc <- runReflexGtk gtkApplication (Just argv) $ myReactiveCode gtkApplication
case rc of
0 -> exitSuccess
n -> exitWith $ ExitFailure $ fromIntegral n
Inside the call to runReflexGtk
you can assume that GTK has been initialized and start creating GTK widgets:
myReactiveCode :: (MonadReflexGtk t m) => Application -> m ()
myReactiveCode gtkApplication = do
window <- runGtk $ applicationWindowNew gtkApplication
box <- runGtk $ boxNew OrientationVertical 0
containerAdd window box
input1 <- runGtk entryNew
input2 <- runGtk entryNew
output <- runGtk $ labelNew Nothing
runGtk $ boxPackStart box input1 False False 0
runGtk $ boxPackStart box input2 False False 0
runGtk $ boxPackStart box output False False 0
Note that we use runGtk
to execute GTK functions instead of relying on liftIO
. This is because GTK functions are required to be run with the correct thread local context. reflex-gi-gtk internally uses multiple threads in a way that makes it hard to predict in which thread a given piece of code will run. runGtk
will ensure that the lifted IO
action is always run in the appropriate threading context for GTK functions.
You can also start obtaining reactive inputs from widget signals:
text1 <- dynamicOnSignal "" input1 #changed $ \fire -> entryGetText input1 >>= fire
text2 <- dynamicOnAttribute input2 #text
This creates to Dynamic
s containing texts. text1
is constructed based on the #changed
event of the first input Entry
while text2
is bound to the attribute #text
of input2
, but overall the effect will be the same. Both text1
and text2
will always contain the text entered into their respective input Entry
.
Once we have obtained reactive inputs, we can of course manipulate them just as any other reactive values:
let combinedText = liftA2 (<>) text1 text2
In the end, we can also render dynamic values to attributes of GTK widgets:
sink output [#label :== combinedText]
Don't forget to call widgetShowAll
on your window at an appropriate time:
_ <- gtkApplication `on` #activate $ widgetShowAll window
pure ()
When you compile and run your program above, you should see your two input boxes and the appropriate, dynamically updated output label.
This is of course a very basic example. For more information you can look at a more elaborate example in the example
directory or at the haddock documentation on hackage.