Giter Club home page Giter Club logo

gi-gtk-declarative's Introduction

gi-gtk-declarative's People

Contributors

andreabedini avatar cblp avatar dependabot[bot] avatar dretch avatar eamsden avatar guibou avatar guygastineau avatar juhp avatar locallycompact avatar ludat avatar marhop avatar owickstrom avatar zenhack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gi-gtk-declarative's Issues

childless window to be used as canvas

Hello sir,

This is a wonderful project. Thank you!

I was just wandering how one can create an empty window (without any child widgets) in order to just paint on it like in the Cairo clock example.

I tried something like below but without success:

view' :: State -> AppView Event
view' _ = widget Window [#title := "drawing" , on #deleteEvent (const (True, Close)), #defaultWidth := 400, #defaultHeight := 300]

Regards,
A

Fail to build examples using stack on nix

Not sure what's causing this

[nix-shell:~/Source/github.com/owickstrom/gi-gtk-declarative]$ stack build --resolver nightly-2018-11-13
gi-gtk-declarative-app-simple-0.2.0: unregistering (local file changes: src/GI/Gtk/Declarative/App/Simple.hs)
gi-gtk-declarative-app-simple-0.2.0: configure (lib)
gi-gtk-declarative-app-simple-0.2.0: build (lib)
gi-gtk-declarative-app-simple-0.2.0: copy/register
examples-0.2.0: configure (exe)
examples-0.2.0: build (exe) 
Completed 2 action(s).          
Log files have been written to: /home/lc/Source/github.com/owickstrom/gi-gtk-declarative/.stack-work/logs/

--  While building package examples-0.2.0 using:
      /home/lc/.stack/setup-exe-cache/x86_64-linux-nix/Cabal-simple_mPHDZzAJ_2.4.0.1_ghc-8.6.2 --builddir=.stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1 build exe:example --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1
    Logs have been written to: /home/lc/Source/github.com/owickstrom/gi-gtk-declarative/.stack-work/logs/examples-0.2.0.log

    Configuring examples-0.2.0...
    Warning:
        This package indirectly depends on multiple versions of the same package. This is very likely to cause a compile failure.
          package typed-process (typed-process-0.2.3.0-DB3YotUmiF53GO4zJHIe3a) requires async-2.2.1-4fWSSJ8SFccIxQJVItkRnX
          package streaming-commons (streaming-commons-0.2.1.0-Gjsp3ARrjdWm3PJDtVFaz) requires async-2.2.1-4fWSSJ8SFccIxQJVItkRnX
          package conduit-extra (conduit-extra-1.3.0-GscXuLj0LnaAQPH0wUQEXK) requires async-2.2.1-4fWSSJ8SFccIxQJVItkRnX
          package pipes-concurrency (pipes-concurrency-2.0.12-a2yO6AToml9LT0f6bCvWO) requires async-2.2.1-CWXVJHwJMKSI5YTkcbFhmH
          package gi-gtk-declarative-app-simple (gi-gtk-declarative-app-simple-0.2.0-LieK8RsLL2l5E3gFPq37Ze) requires async-2.2.1-CWXVJHwJMKSI5YTkcbFhmH
          package examples (examples-0.2.0) requires async-2.2.1-CWXVJHwJMKSI5YTkcbFhmH
          package resourcet (resourcet-1.2.2-F7rwLP4LeKZDWC147Y2OKD) requires exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12
          package pipes (pipes-4.3.9-8wacBWbqZInCiGw6SkJvVi) requires exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12
          package lens (lens-4.17-8fLX0VNMMHc1mA5LfhbHd8) requires exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12
          package free (free-5.1-6ep10VQBTbdEK8XZsGqaIw) requires exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12
          package conduit (conduit-1.3.1-8C5VGtcqiJC7UDbOgUmLWv) requires exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12
          package pipes (pipes-4.3.9-JpGgE9DNmLLHzn972uONS9) requires exceptions-0.10.0-DHZ8jRIWmju3Tn1kS7T9Xv
          package unordered-containers (unordered-containers-0.2.9.0-L4dSDXPacp4A6ajMikU5GH) requires hashable-1.2.7.0-6U3VnAjAfQC5KkN2X3oAD1
          package async (async-2.2.1-CWXVJHwJMKSI5YTkcbFhmH) requires hashable-1.2.7.0-6U3VnAjAfQC5KkN2X3oAD1
          package unordered-containers (unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package semigroupoids (semigroupoids-5.3.1-ITLTc9xnUUXHEMO4GDBuYe) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package scientific (scientific-0.3.6.2-KIIlODNgUeO9twnHef2PBn) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package mono-traversable (mono-traversable-1.0.9.0-4dGBHfsSsSt9xrsSxPseyC) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package lens (lens-4.17-8fLX0VNMMHc1mA5LfhbHd8) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package foldl (foldl-1.4.5-7pM1wM7mDurBNwWpbR19dK) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package async (async-2.2.1-4fWSSJ8SFccIxQJVItkRnX) requires hashable-1.2.7.0-CWI8VZuQTAYWIQTQQtqCV
          package pipes (pipes-4.3.9-JpGgE9DNmLLHzn972uONS9) requires mmorph-1.1.2-1XKbJejgVfn63jvndItiSw
          package pipes (pipes-4.3.9-8wacBWbqZInCiGw6SkJvVi) requires mmorph-1.1.2-2SXg3Mu2jQSB4W1Qkd7vm4
          package pipes-extras (pipes-extras-1.0.15-7jraOcnyJBsGNHTnL4gCli) requires pipes-4.3.9-8wacBWbqZInCiGw6SkJvVi
          package pipes-concurrency (pipes-concurrency-2.0.12-a2yO6AToml9LT0f6bCvWO) requires pipes-4.3.9-JpGgE9DNmLLHzn972uONS9
          package gi-gtk-declarative-app-simple (gi-gtk-declarative-app-simple-0.2.0-LieK8RsLL2l5E3gFPq37Ze) requires pipes-4.3.9-JpGgE9DNmLLHzn972uONS9
          package examples (examples-0.2.0) requires pipes-4.3.9-JpGgE9DNmLLHzn972uONS9
          package transformers-base (transformers-base-0.4.5.2-JUlqsL73OImGGUE2DcC4Q8) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package semigroupoids (semigroupoids-5.3.1-ITLTc9xnUUXHEMO4GDBuYe) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package mmorph (mmorph-1.1.2-2SXg3Mu2jQSB4W1Qkd7vm4) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package lens (lens-4.17-8fLX0VNMMHc1mA5LfhbHd8) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package kan-extensions (kan-extensions-5.2-EggfbLk6czXLSibVJH3Wi8) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package invariant (invariant-0.5.1-KU4SlDml78tLhJycFuXNW0) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package exceptions (exceptions-0.10.0-7rupAzx2MQN9ks4RM1dl12) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package comonad (comonad-5.0.4-LgAoXN8uHo4Lw150anSHrf) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package adjunctions (adjunctions-4.4-BGM5FUQxBSYDQps3g4IHiP) requires transformers-compat-0.6.2-2aGr2H01tr03oYIwD5LYXX
          package mmorph (mmorph-1.1.2-1XKbJejgVfn63jvndItiSw) requires transformers-compat-0.6.2-JV1ddSnlm3mL2xw4J4K8Kw
          package exceptions (exceptions-0.10.0-DHZ8jRIWmju3Tn1kS7T9Xv) requires transformers-compat-0.6.2-JV1ddSnlm3mL2xw4J4K8Kw
          package semigroupoids (semigroupoids-5.3.1-ITLTc9xnUUXHEMO4GDBuYe) requires unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin
          package mono-traversable (mono-traversable-1.0.9.0-4dGBHfsSsSt9xrsSxPseyC) requires unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin
          package lens (lens-4.17-8fLX0VNMMHc1mA5LfhbHd8) requires unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin
          package invariant (invariant-0.5.1-KU4SlDml78tLhJycFuXNW0) requires unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin
          package foldl (foldl-1.4.5-7pM1wM7mDurBNwWpbR19dK) requires unordered-containers-0.2.9.0-BRWkoSTuML1cQdpep6Oin
          package gi-gtk-declarative (gi-gtk-declarative-0.2.0-IelyUDt11dT5QHNzlfslpR) requires unordered-containers-0.2.9.0-L4dSDXPacp4A6ajMikU5GH
    Preprocessing executable 'example' for examples-0.2.0..
    Building executable 'example' for examples-0.2.0..
    [ 1 of 10] Compiling AddBoxes         ( AddBoxes.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/AddBoxes.o ) [GI.Gtk.Declarative.App.Simple changed]
    [ 2 of 10] Compiling CSS              ( CSS.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/CSS.o ) [GI.Gtk.Declarative.App.Simple changed]
    [ 3 of 10] Compiling Exit             ( Exit.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/Exit.o ) [GI.Gtk.Declarative.App.Simple changed]
    [ 4 of 10] Compiling FileChooserButton ( FileChooserButton.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/FileChooserButton.o ) [GI.Gtk.Declarative.App.Simple changed]
    [ 5 of 10] Compiling Functor          ( Functor.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/Functor.o ) [GI.Gtk.Declarative.App.Simple changed]
    [ 6 of 10] Compiling Hello            ( Hello.hs, .stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/example/example-tmp/Hello.o ) [flags changed]
    
    /home/lc/Source/github.com/owickstrom/gi-gtk-declarative/examples/Hello.hs:45:14: error:
        • Couldn't match type ‘pipes-4.3.9:Pipes.Internal.Proxy
                                 () a0 () a0 IO r0’
                         with ‘Proxy () Event c' c m ()’
          NB: ‘Proxy’ is defined in ‘Pipes.Internal’ in package ‘pipes-4.3.9’
              ‘pipes-4.3.9:Pipes.Internal.Proxy’
" ============================================================================
                is defined in ‘Pipes.Internal’ in package ‘pipes-4.3.9’
          Expected type: Proxy () Event c' c m ()
            Actual type: pipes-4.3.9:Pipes.Core.Pipe a0 a0 IO r0
        • In the second argument of ‘(>->)’, namely ‘Pipes.delay 1.0’
          In the second argument of ‘(&)’, namely ‘(>-> Pipes.delay 1.0)’
          In the expression:
            cycle ["Joe", "Mike"] & map (\ n -> (Greet ("Hello, " <> n)))
              & each
              & (>-> Pipes.delay 1.0)
        • Relevant bindings include
            greetings :: Proxy a' a c' c m () (bound at Hello.hs:41:3)
       |
    45 |       & (>-> Pipes.delay 1.0)
       |              ^^^^^^^^^^^^^^^

Changing runInWindow to return the final state.

Hello, I'm new to gtk. I'm looking at situations like the FileChooser example and wondering how best to compose it with larger applications. Would it make sense to change run or runInWindow to return the final state?

runInWindow :: Typeable event => Gtk.Window -> App state event -> IO state

so that the window can run to completion as if it were a sub-app, provide a result (the filepath), and then the parent application's logic can interpret the result how it wants? Are there reasons this may/may not make sense as a strategy?

Thanks

Want to add support for 'gridAttachNextTo' for Grid somehow

First of all, thanks for this library!
It gives me good opportunity and motivation to look into GUI applications.

What I want

I want to use gridAttachNextTo instead of 'gridAttach' so that I don't have to change leftAttach/topAttach manually for each children.
This is neccesary for me because I want to change each width/height in runtime.
If this feature is determined as 'unnecessary' for library, I will keep this just mime.

Suggestion

I'm thinking 2 way:

  • Make new widget (e.g. GridNextTo or something like that) that uses 'gridAttachNextTo' in appendChild method of IsContainer
  • Add new constructor of GridChildProperties (e.g. GridChildPropertiesNextTo) which contains necessary properties i.e. sibling/side, and use them with gridAttachNextTo in appendChild

Current status (at time I made this issue)

I've tried later one in my fork and seems working fine for limited case.
It doesn't supports 'sibling' feature which is big problem if it will be part of the library.

But I'm not sure how to implement 'sibling' (and also tests are not implemented yet), so I want to get help about this.

Note that I'm willing to change current implementation or throw away it if this functionality could be part of this library.

Parameterize Streaming Implementation for App.Simple?

So, Pipes seems fine, but we have a lot of options for streams in Haskell.
Personally I like Streamly a lot.

Streamly has a good tutorial for interop with other libraries, so I have just been converting my Streamly types to Pipes types as the last step before they are fed in as producers. I don't really mind this, but it got me thinking about generalizing the producers in your state reducer. I haven;t looked at the code for the reducer yet, but I don't mind trying to figure it out.

Anyway, If people think this is a good idea I will work on it. If this is somehow not desirable, then I will just leave it alone ;)

Realize signal fails to trigger.

Below is the same program written in vanilla gi-gtk and then again using gi-gtk-declarative. The vanilla gtk behaves as expected, outputting "Realized!" to the console. The declarative version fails to trigger the realize signal.

Working vanilla version:

{-# LANGUAGE OverloadedStrings, OverloadedLabels #-}
import GI.Gtk as Gtk hiding (main)
import qualified GI.Gtk as Gtk

onRealize :: IO ()
onRealize = putStrLn "Realized!"

main = do
    _ <- Gtk.init Nothing

    let mkChild = do
            gl <- gLAreaNew
            _ <- on gl #realize onRealize
            return gl

    window <- do
        w <- windowNew WindowTypeToplevel
        windowSetDefaultSize    w 600 600
        windowSetTitle          w "This triggers realize signal."
        _ <- on w #deleteEvent $ \_ -> mainQuit >> return True
        child <- mkChild
        containerAdd w child
        return w

    widgetShowAll window
    Gtk.main

Non-working declarative version:

{-# LANGUAGE OverloadedStrings, OverloadedLists, OverloadedLabels, LambdaCase #-}
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative
import GI.Gtk.Declarative.EventSource
import GI.Gtk.Declarative.State

onRealize :: IO Bool
onRealize = putStrLn "Realized!" >> return True

main = do
    _ <- Gtk.init Nothing

    let child = widget Gtk.GLArea [onM #realize $ \_ -> onRealize]

    window <- do
        let w :: Bin Gtk.Window Bool
            w = bin Gtk.Window
                    [ #title := "This fails to trigger realize signal."
                    , #widthRequest := 600
                    , #heightRequest := 600
                    , on #deleteEvent $ \_ -> (True,False)
                    ]
                    child
        someState <- create w
        sub <- subscribe w someState $ \case
            False -> Gtk.mainQuit
            True  -> return ()
        someStateWidget someState
    Gtk.widgetShowAll window
    Gtk.main

I first encountered the problem while using the gi-gtk-declarative-app-simple framework as this seems to be the only documented way to use gi-gtk-declarative. (Is that a bug?) But I removed that layer while trying to debug this issue.

Window is not destroyed when event loop stops, only when program exits

In the code below, after clicking the close button of the window, the window will not close, staying open while the program is still running. Only after pressing enter on the command prompt, unblocking the getLine function, does the window close when the process exits. For my use case I need the window to close when the "run" function returns.

{-# LANGUAGE OverloadedLabels  #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import           Data.Function                  ( (&) )
import           Data.Text                      ( Text )
import           Pipes
import qualified Pipes.Extras                  as Pipes
import           Control.Monad                  ( void )

import           GI.Gtk                         ( Label(..)
                                                , Window(..)
                                                )
import           GI.Gtk.Declarative
import           GI.Gtk.Declarative.App.Simple

type State = ()

data Event = Closed

view' :: State -> AppView Window Event
view' _ =
  bin
      Window
      [ #title := "Hello"
      , on #deleteEvent (const (True, Closed))
      , #widthRequest := 400
      , #heightRequest := 300
      ]
    $ widget Label [#label := "Nothing here yet."]

update' :: State -> Event -> Transition State Event
update' _ Closed      = Exit

main' :: IO ()
main' = void $ run App { view         = view'
                      , update       = update'
                      , inputs       = []
                      , initialState = ()
                      }

main = do
    main'
    getLine

In the following gi-gtk program that does not happen:

{-# LANGUAGE OverloadedStrings, OverloadedLabels #-}

{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}

{- Hello World example of GTK+ documentation. For information please refer to README -}

module Main where

-- import qualified Data.Text as T
import Data.Text ()

import qualified GI.Gio as Gio
import qualified GI.Gtk as Gtk
import Data.GI.Base
import Control.Concurrent

printHello :: IO ()
printHello = putStrLn "Hello, World!"

activateApp :: Gtk.Application -> IO ()
activateApp app = do
  w <- new Gtk.ApplicationWindow [ #application := app
                                 , #title := "Haskell Gi - Examples - Hello World"
                                 , #defaultHeight := 200
                                 , #defaultWidth := 200
                                 ]

  bbox <- new Gtk.ButtonBox [ #orientation := Gtk.OrientationHorizontal ]
  #add w bbox
  
  btn <- new Gtk.Button [ #label := "Hello World!"]
  on btn #clicked printHello
  --on btn #clicked $ Gtk.widgetDestroy w
  #add bbox btn
  
  #showAll w
  return ()

main' :: IO ()
main' = do
  app <- new Gtk.Application [ #applicationId := "haskell-gi.examples.hello-world"
                             , #flags := [ Gio.ApplicationFlagsFlagsNone ]
                             ]
  on app #activate $ activateApp app
  Gio.applicationRun app Nothing
  return ()

main = do
    main'
    getLine

GTK4?

So in #90 I gave an upper bounds of <4 to the project.

I am really not well versed in GTK anyway (although this project is helping with that).

I am wondering if anyone has tested this with GTK4. I expect it to work out of the box. Is that correct?

If it works with any GTK (or at least 3 and 4) should the upper version bounds be relaxed?
I guess users can just specify the verion of gi-gtk they want to use in order to stick with one or the other. How would we test with both versions of GTK in the CI?

Cursor drift in Gtk.Entry in Todo example app

If you take the app built in https://haskell-at-work.com/episodes/2019-01-10-purely-functional-gtk-part-1-hello-world.html and https://haskell-at-work.com/episodes/2019-01-19-purely-functional-gtk-part-2-todo-mvc.html it manifests the following issue:

Peek 2019-07-23 12-35

After I selected the text I started typing "123", but the cursor moved to the beginning after I typed "1" and I got "231".

The code for the app looks resonable, that's why I raise this issue here.

I have the app in a buildable form at niteria@2ccd690

How do I start a background process and monitor it in gi-gtk-declarative?

Hi, I hope questions are fine here. Please tell me where to put them otherwise.

I want to start a long-running process in the background (think about a big background download). I want my declarative UI to display the progress and react to its completion.

With gi-gtk-declarative-app-simple, I can apparently create a background process that triggers one event using the Transition constructor's second argument. But that doesn't cover my case exactly, because how would I update its progress, say, at regular intervals?

Is the proposed solution to create regular "tick" events using the inputs list and check background processes that way?

Widget's internal state is not exposed in the `customSubscribe`

Hi.

I'm constructing the Tablet, a complex declarative widget for a strategy game. The map/terrain screen is a Gtk.Layout in which the units (Gtk.EventBoxes) are put, moved or deleted. The idea is to have the declarative widget to generate events when the user clicks/interacts with the units. I want the widget to generate something like TabletWidgetObjectClicked ObjectId, so I store and calculate from the Parameters a collection of special data in the declarative widget's internal state (SomeState).

What is the recommended way to write the equation for the customSubscribe function:
customSubscribe :: customData -> widget -> (event -> IO ()) -> IO Subscription
in which I'm supposed to establish all the individual handlers for the dynamically generated EventBoxes (tagged with ObjectIds) and in some sense to map them to the various custom widget's events?

The problem is that the internal state SomeState is not exposed in the customSubscribe (the only handle I've got is my toplevel Gtk.Box from which everything descends), but I'm sure I'm missing something.

Thanks in advance.

[Dependencies] Build fails given current dependency constraints

As you can see on this SO question, currently this library fails to build given a third party dependency.

The upstream issue was this one and it's already fixed

Regarding this library:

  • In the dependencies, it's specified: haskell-gi (>=0.21 && <0.24)
  • The build issue was fixed on haskell-gi 0.24.4 which can't be used given the constraints

The current workaround is running stack build --allow-newer or cabal build --allow-newer, but this is far from ideal.

In my pet program, everything looks fine, so I can't be sure that bumping the dependencies won't break anything

Deprecate 'afterCreate'

It was basically a hack, when custom widgets would be the right solution.

Maybe we should adopt CustomWidget from Komposition. It makes it much easier to build a custom widget, without having to do a lot of Typeable stuff:

https://github.com/owickstrom/komposition/blob/83bc84f353ca353f73f543e4496f6854fd864447/src/Komposition/UserInterface/GtkInterface/RangeSlider.hs#L29-L54

I think it could be made even easier by not requiring the internal custom state to be pattern matched on when using it in your custom widget.

How do I make a dialog with its own local state?

Hello,
I am trying out gi-gtk-declarative for my own program.
I want to have some dialog which does complex configuration - I'd appreciate if it was a separate dialog.
So far, I set up my event to run a runLoop - but it does not seem to close properly.
How do I make a proper dialog with its own local state?
Thanks in advance!

Remove properties from nodes when modifying

If a widget is patched its old attributes are not removed (when they aren't present in the new widget), only the existing ones are overwritten. This might require the constructors of SingleWidget and Container to constrain attributes to the clear operation, in addition to construct and set.

[Question] Handling forms

Hi! First of all, I want to thank you for this awesome library. I never thought that writing UI in Haskell could be this easy.

I'm having a bit of trouble when it comes to the design and implementation of forms, and I'm unable to find a full example of this use case, so I'm not sure about how to implement it.

Currently, I have a view with a few Entries, each for a specific input - let's say name, surname, email, and a submit Button.

  • The first solution that comes to mind is defining a field in my State that holds the current value of each Entry in some kind of Map. I can get the current Text for each Entry using the onM #changed function and entryGetText, then I can fire an Event that updates the State with the new value. This approach has a few advantages like on the fly validation and works fine for a small project, but let's say that my application has 20/30 forms. Would this scale?

  • What I would like to do (don't know if this is the right way™) is to use a function that collects the text inside all my Entries and builds some kind of Map (id -> value like in HTML forms) and fires an Event, something like UserRegistrationEvent Fields, where Fields is a map of ids to values. This second approach would reduce the amount of code inside the State record but sacrifices the on the fly validation. Also, since Fields is a kind of Map I'm a little worried about safety: I need to make sure that the values that I look up inside the map are valid - for example, if I look up phone-number in my example I could get a runtime error.

Please, let me know what you think. I'll update the issue with some code snippets from my use case as soon as possible.

Also: I looked into the React-Redux ecosystem since gi-gtk-declarative uses a similar design pattern for UIs and I found redux-form. It would be interesting to have a similar library for this amazing project.
Disclaimer: I primarily use Angular for my frontend projects so I'm not very confident in porting that library to Haskell

Fail to run FileChooserButton on nix

I'm new to nix so I don't know what I'm doing exactly, but if I nix-build and then try to run the ./result/bin/example FileChooserButton I get the following error:

lc@sunwell ~/S/g/o/gi-gtk-declarative> ./result/bin/example FileChooserButton

(example:8360): GLib-GIO-ERROR **: 13:45:32.533: Settings schema 'org.gtk.Settings.FileChooser' is not installed

fish: “./result/bin/example FileChoose…” terminated by signal SIGTRAP (Trace or breakpoint trap)

Ignored exception in update handler

The following code sample exhibits a scenario where exceptions are ignored and break event handling.

{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}

import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative (Attribute (..), bin, container, on, widget)
import GI.Gtk.Declarative.App.Simple (App (..), AppView, Transition (..), run)
import qualified Data.Text as Text
import Control.Monad (void)

data AppEvent = Inc | Inc2

view' ::
  Int ->
  AppView Gtk.Window AppEvent
view' i =
  bin
    Gtk.Window
    [ #title := "Tablet UI"
    ]
    $ container
      Gtk.Box
      []
      [ widget
          Gtk.Label
          [#label := (Text.pack $ show i)],
        widget
          Gtk.Button
           [#label := "Click",
            on #clicked Inc
           ],
        widget
          Gtk.Button
           [#label := "Click 2",
            on #clicked Inc2
           ]
      ]

update' ::
  Int ->
  AppEvent ->
  Transition Int AppEvent
update' i Inc = Transition (i+1) (pure Nothing)
update' i Inc2 = error "die"

main :: IO ()
main = do

  void $ run $ App
    { update = update',
      view = view',
      inputs = [],
      initialState = 0
    }

It generates a label and two click buttons. The first one is generating the Inc event and the second one is generating the Inc2 event.

Unfortunately, the update' function raises an exception for the Inc2 event.

I was expecting a runtime exception when clicking on the second button, but instead, the event handling is now broken and all of that happen silently. Clicking on the first button is working until we click on the second button, and then it does not work anymore.

Is it possible to have a fatal exception instead, or at least a message in the console such as "An exception occurred in the event handler: blablabla" if you think that the event handler must recover from exceptions.

Thank you.

Wrap Gtk.Notebook

I found myself wanting to use a gtk notebook (tabs), but it looks like this widget will need a little bit of custom logic to be able to add children.

I tried adding the necessary instances myself, but here's where I got stuck: StateTree seems to assume all parent/child relationships fit into the widget/bin/container ontology, but notebooks have "pages" which have a main contents widget, but also the tab itself can be an arbitrary widget, and there's also a "menu label" widget that can be specified. Furthermore, there is no widget caled "page"; the relevant functions just take more than one widget when adding in the same call. So I don't know how to construct the SomeState for a notebook, or write the EventSource and Patchable instances.

Any insights?

Relevant gtk docs:

https://developer.gnome.org/gtk3/stable/GtkNotebook.html#gtk-notebook-append-page

add to Stackage

How about adding gi-gtk-declarative and gi-gtk-declarative-app-simple to Stackage Nightly and LTS?

Some initial build issues re: Tom Ellis

Working on some initial build issues right now.

To my surprise, iirc, on at least 9.6.2 GHC on Windows, there were dependencies unmet in 9.6.2 GHC that meant that the library simply wouldn't attempt to compile, and iirc, this was the case for every other GHC version I tried.

On Linux, however, at least on 9.4.6, either my installed gi-cairo / gi-glib versions weren't working, or there seems to be some kind of build issue. Similar, but different build issues arise up to 9.2.8, now trying 8.10.7...

Nope.

GI/GLib/Functions.hs:5083:34: error:
• Couldn't match type ‘Char’ with ‘CInt’
Expected type: Ptr CInt
Actual type: Ptr Char
• In the first argument of ‘g_unicode_canonical_ordering’, namely
‘string'’
In a stmt of a 'do' block: g_unicode_canonical_ordering string' len
In the second argument of ‘($)’, namely
‘do let len = fromIntegral $ length string
string' <- packStorableArray string
g_unicode_canonical_ordering string' len
freeMem string'
....’
|
5083 | g_unicode_canonical_ordering string' len
| ^^^^^^^
Error: cabal: Failed to build gi-glib-2.0.25 (which is required by
gi-gtk-declarative-0.7.1). See the build log above for details.

Via pacman -Ss:

.
extra/haskell-gi-glib 2.0.29-8 [installed]
GLib bindings
extra/haskell-glib 0.13.8.2-49 [installed]
Binding to the GLIB library for Gtk2Hs.

core/glib2 2.76.4-1 [installed]
Low level core library
core/glib2-docs 2.76.4-1
Low level core library - documentation
core/glibc 2.38-3 [installed]


Not sure if you can reproduce on your side, I'm not going lower than 8.10.7...

ComboBoxText support

I've had some success adding support for Gtk.Grid by basing it on the Gtk.Box container implementation, but I'm not sure what the best approach for ComboBoxText would be.

The difference between ComboBoxText and Gtk.Grid is that there's nothing that's a Widget inside of ComboBoxText (at least on the surface, I suspect it might be implemented in terms of ComboBox). That got me stuck when I needed to implement Patchable for the item type.

Right now I think that CustomWidget route is a promising one, but maybe I'm missing something.

Error while buillding with stack - Could not find module ‘GI.GModule.Structs.Module’

I wanted to try out gi-gtk-declarative so I created an empty stack project and added gi-gtk-declarative and gi-gtk-declarative-app-simple as dependencies (using the latest lts resolver 17.10) but unfortunately it fails to build stating that the gdk pixbuf module cannot build:

gi-gdkpixbuf                 > [11 of 25] Compiling GI.GdkPixbuf.Objects.PixbufNonAnim
pipes-concurrency            > Registering library for pipes-concurrency-2.0.12..
gi-gdkpixbuf                 > [12 of 25] Compiling GI.GdkPixbuf.Objects.PixbufSimpleAnim
gi-gdkpixbuf                 > [13 of 25] Compiling GI.GdkPixbuf.Objects.PixbufSimpleAnimIter
gi-gdkpixbuf                 > [14 of 25] Compiling GI.GdkPixbuf.Structs.PixbufFormat[boot]
gi-gdkpixbuf                 > [15 of 25] Compiling GI.GdkPixbuf.Objects.PixbufLoader
gi-gdkpixbuf                 > [16 of 25] Compiling GI.GdkPixbuf.Structs.PixbufModule[boot]
gi-gdkpixbuf                 > [17 of 25] Compiling GI.GdkPixbuf.Callbacks
gi-gdkpixbuf                 > [18 of 25] Compiling GI.GdkPixbuf.Objects.Pixbuf
gi-gdkpixbuf                 > [19 of 25] Compiling GI.GdkPixbuf.Objects
gi-gdkpixbuf                 > [20 of 25] Compiling GI.GdkPixbuf.Structs.PixbufModule
gi-gdkpixbuf                 >
gi-gdkpixbuf                 > /tmp/stack-e7459d0a8a8f5ac7/gi-gdkpixbuf-2.0.24/GI/GdkPixbuf/Structs/PixbufModule.hs:194:1: error:
gi-gdkpixbuf                 >     Could not find module ‘GI.GModule.Structs.Module’
gi-gdkpixbuf                 >     Use -v (or `:set -v` in ghci) to see a list of the files searched for.
gi-gdkpixbuf                 >     |
gi-gdkpixbuf                 > 194 | import qualified GI.GModule.Structs.Module as GModule.Module
gi-gdkpixbuf                 >     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
gi-gdkpixbuf                 >
Progress 2/7

--  While building package gi-gdkpixbuf-2.0.24 (scroll up to its section to see the error) using:
      /tmp/stack-e7459d0a8a8f5ac7/gi-gdkpixbuf-2.0.24/.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.2.1.0/setup/setup --builddir=.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.2.1.0 build --ghc-options " -fdiagnostics-color=always"
    Process exited with code: ExitFailure 1

has anyone else had this issue and are there any workarounds?

Patchable for Box ignores BoxChildProperties?

I was trying to understand how the pieces fit together and looking at the implementation of Patchable for Box got me wondering why BoxChildProperties isn't taken into account.

I don't have a real world example that demonstrates it should be, but I can come up with something contrived that demonstrates the need. Say the padding property is derived from the current application state. IIUC (and a simple program seems to confirm this) an empty patch will be computed and nothing will update.

Am I misunderstanding something?

(Btw, thank you for the library, the library looks awesome, I really want to try building UIs in this style)

No window appears when running App.Simple application executable

I have created a standalone application based on GI.Gtk.Declarative.App.Simple. Its sources are copy paste of the Hello.hs example.

You can find the repository here.

I observe the following:

  1. Trying to build and run it produces no window at all
$ stack build && stack exec amfora-hs
# No window, have to interrupt
^C^C
$
  1. Trying to load it inside stack ghci and running main manually somehow does produce a window (greeting names cycle, all's working), but then there's another problem: when I click on x (close) button, the application hangs with x pressed down. In ghci I can see that main has returned, but window stays on screen

To be sure that all this is something related to gi-gtk-declarative, I tried to replace main contents with some simple hello-world window from haskell-gi samples and it does work both with stack exec and stack ghci: window is shown and closed.

I'm not sure why no window appears for the case 1., but as for window freeze I suspect that this might have to do with the fact that there's no listener to window #destroy event in App.Simple code, but I might be mistaken.


Stack version: 1.9.3
Resolver: lts-13.2 (ghc version 8.6.3)
gi-gtk-declarative: 0.4.1
gi-gtk-declarative-app-simple: 0.4.0

ListBox content change leads to cursor position reset

I have a ListBox with some children. Now I select a row in the ListBox, and press a button that changes the content of the selected row (say, by changing a Label to "installed"). This seems to completely reset the ListBox selection (so that no item is selected).
I sort of get the problem here, but I'd love to somehow manage the selection myself, and ideally by using an index, so the selection is kept, even on change.

An example without `App.Simple`?

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.

marked broken in nix

I can not use gi-gtk-declarative with the current version of nix (at the time of writing):

"url": "https://github.com/NixOS/nixpkgs-channels.git",
"ref": "nixos-unstable",
"rev": "e19054ab3cd5b7cc9a01d0efc71c8fe310541065"

When gi-gtk, gi-gtk-declarative and gi-gtk-declarative-app-simple are added to the project.cabal and default.nix file, the following error is reported:

$ nix-shell 
error: Package ‘gi-gtk-declarative-0.5.0’ in 
...hackage-packages.nix:93451 is marked as broken, refusing to evaluate.

Adding Buttons to/Using FileChooserDialog

Hello! So, I've been trying to use FileChooserDialog a bit, but I'm having some trouble with it. By default, it seems as though a FileChooserDialog doesn't have any buttons at the bottom of the dialog, and I'm not sure how to add, say, a Cancel and a Save button. I was wondering if I'd have to create a new custom widget to do that, or is there an easier way?

My end goal is to have a MenuBar with a File > Save As button, but I'm not sure how to go about implementing the "Save As" part using FileChooserDialog; I'm not sure how to add the Cancel and Save buttons at the bottom or how to know when the user is done choosing the filename/location for the file to be saved to.

Add event listener support

It should be possible to attach event listeners to declarative widgets somehow. Some ideas/references:

  • Be able to attach functions Event -> IO () on any widgets, similar to purescript-vdom. I can see the benefit of this being very generic, but it also works against the "purely functional" stance of this library.
  • Have markup parameterized by a single event type, where the user maps any event into a value of that type:
    data MyEvent = LabelClicked | LabelHovered
    
    gui :: Markup MyEvent
    gui = node Label [ #label := "Click Me!"
                     , on #clicked (\_ -> LabelClicked) 
                     , on #moveCursor (\_ -> LabelHovered) 
                     ]
    Then when the Markup is diffed and rendered you may get a Chan MyEvent back. Or, you could create the Chan MyEvent yourself and pass that into rendering functions (to have a single channel survive re-renders.)

I'm leaning towards the second option, but not very sure.

One concern of the second approach is how well it scales in terms of large markup structures. You'd have to map all events into a single sum data type, risking the state space explosion as can happen in Elm. For instance, a "clock" component might tick every second, but the user of the clock should not have to deal with all the tick events and pass them along to the clock. One could wrap the clock implementation in a custom declarative widget data type (creating instances for Patchable etc) and only expose certain events, but then you'd have to drop down to the imperative GTK+ layer.

In that sense, keeping the library's support for events equivalent to that if purescript-vdom might be a good idea, and then possibly build more opinionated framework on top, e.g. one Elm-style framework and one like Halogen.

Missing an example for GtkTextView

Great stuff!

...not knowing much about GObject and the internals of haskell-gi, is it straight forward to build a simple example for GtkTextView ?

If not, that is, if some layer has to be implemented in haskell-gi or gi-gtk-declarative, could you provide an example for this too ?

widget TextView [#buffer := ....what-goes-here-?.... ]

Best,
Joerg

Example for pane in docs is out of date

Hey! Was just playing around and noticed the pane type has changed since the docs were written.

This doesn't work now...

paned
  [#wideHandle := True]
  (pane (Resize True) (Shrink True) $ widget Label [#label := "Left"])
  (pane (Resize True) (Shrink True) $ widget Label [#label := "Right"])

...but this does:

paned
  [#wideHandle := True]
  (pane PaneProperties { resize = True, shrink = True } $ widget Label [#label := "Left"])
  (pane PaneProperties { resize = True, shrink = True } $ widget Label [#label := "Right"])

If there is a way to directly submit a PR with the change I'd be happy to, if not I hope this is vaguely helpful.

Windows10: FileChooserButton example sends LF to stderr

I do not know where the problem lies but, on Windows 10, the FileChooserButton example (and only that example) somehow sends LF characters to stderr when the dialog box is first drawn (about 7 or 8 LF characters) and when the buttons are clicked (1 or 2 LF characters).

For example (with the example's dialog box immediately closed once it appears):

>stack exec example -- FileChooserButton 2> temp.txt
>notepad++ temp.txt

yields:
image

(This is with the package up to commit 1e96813 and built with resolver nightly-2018-12-17 (GHC 8.6.2)).

Implement TreeView with patchable models

Basic Proposal

A GtkTreeView is associated with an instance of GtkTreeModel which is what actually gets mutated in order to add or remove nodes from the tree (commonly GtkListStore or GtkTreeStore).

One way to handle this would be to have a new declarative widget type View

data ViewWidget widget model event where
  ViewWidget
    :: Typeable widget
    => IsWidget view
    => (Gtk.ManagedPtr widget -> widget)
    -> Vector (Attribute widget event)
    -> model
    -> ViewWidget widget model event

and corresponding smart constructor

viewWidget
  :: ( Patchable (ViewWidget widget model event)
     , Typeable widget
     , Typeable model
     , Typeable event
     , Gtk.IsWidget widget
     , FromWidget (ViewWidget widget model) target
  -> (Gtk.ManagedPtr widget -> widget)
  -> Vector (Atribute widget event)
  -> model
  -> target event

Plus a declarative implementation of columns, lists, and trees.

The Patchable instance for a ViewWidget should recurse into the model and build an operation to update the underlying GtkListStore or GtkTreeStore.

Possible enhancements

  • Newtype wrappers around instantiations of the Model type could be provided to e.g. change the diff algorithm used to generate patches for lists or trees.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.