gi-gtk-declarative 0.2.0 → 0.3.0
raw patch · 17 files changed
+421/−255 lines, 17 filesdep ~basedep ~criteriondep ~gi-glib
Dependency ranges changed: base, criterion, gi-glib
Files
- CHANGELOG.md +5/−0
- bench/Benchmark.hs +7/−2
- gi-gtk-declarative.cabal +17/−4
- src/GI/Gtk/Declarative.hs +5/−1
- src/GI/Gtk/Declarative/Bin.hs +9/−31
- src/GI/Gtk/Declarative/Container.hs +25/−40
- src/GI/Gtk/Declarative/Container/Box.hs +34/−29
- src/GI/Gtk/Declarative/Container/Class.hs +18/−0
- src/GI/Gtk/Declarative/Container/ListBox.hs +14/−0
- src/GI/Gtk/Declarative/Container/MenuItem.hs +34/−23
- src/GI/Gtk/Declarative/Container/Paned.hs +104/−0
- src/GI/Gtk/Declarative/Container/Patch.hs +4/−3
- src/GI/Gtk/Declarative/CustomWidget.hs +56/−0
- src/GI/Gtk/Declarative/Markup.hs +0/−105
- src/GI/Gtk/Declarative/SingleWidget.hs +9/−17
- src/GI/Gtk/Declarative/Widget.hs +62/−0
- src/GI/Gtk/Declarative/Widget/Conversions.hs +18/−0
CHANGELOG.md view
@@ -1,3 +1,8 @@+* 0.3.0+ - Add user documentation+ - Use record for `BoxChild` properties (breaking change!)+ - Use lists for child widgets instead of `MarkupOf` monad (breaking change!)+ - Add support for `Paned` widget * 0.2.0 - Introduce shadow state (breaking change!) - Optimized patching (2x-7x faster!)
bench/Benchmark.hs view
@@ -6,6 +6,7 @@ import Control.Concurrent import Control.Monad import Criterion.Main+import Data.Functor ((<&>)) import Data.Text import qualified GI.Gdk as Gdk import qualified GI.GLib.Constants as GLib@@ -16,10 +17,14 @@ import GI.Gtk.Declarative.State testView :: [Int] -> Widget ()-testView ns = bin Window [] $ container Box [] $ forM_ ns $ \n ->- boxChild True True 10+testView ns = bin Window [] $ container Box [] $ ns <&> \n ->+ BoxChild defaultBoxChildProperties { expand = True+ , fill = True+ , padding = 10+ } $ widget Label [#label := pack (show n), classes ["a", "b"]] +testPatch :: Patchable widget => SomeState -> widget e1 -> widget e2 -> IO SomeState testPatch state oldView newView = case patch state oldView newView of Modify ma -> do ret <- newEmptyMVar
gi-gtk-declarative.cabal view
@@ -1,5 +1,5 @@ name: gi-gtk-declarative-version: 0.2.0+version: 0.3.0 synopsis: Declarative GTK+ programming in Haskell description: A declarative programming model for GTK+ user interfaces, implementing support for various widgets@@ -15,11 +15,19 @@ author: Oskar Wickström maintainer: oskar.wickstrom@gmail.com copyright: Oskar Wickström+homepage: https://github.com/owickstrom/gi-gtk-declarative+bug-reports: https://github.com/owickstrom/gi-gtk-declarative/issues category: Graphics build-type: Simple cabal-version: >=1.10 extra-source-files: CHANGELOG.md+tested-with: GHC == 8.6.3 +source-repository head+ type: git+ location: https://github.com/owickstrom/gi-gtk-declarative++ library exposed-modules: GI.Gtk.Declarative , GI.Gtk.Declarative.Attributes@@ -28,18 +36,23 @@ , GI.Gtk.Declarative.Attributes.Internal.EventHandler , GI.Gtk.Declarative.Attributes.Internal.Conversions , GI.Gtk.Declarative.Bin+ , GI.Gtk.Declarative.CustomWidget , GI.Gtk.Declarative.Container , GI.Gtk.Declarative.Container.Box , GI.Gtk.Declarative.Container.Class+ , GI.Gtk.Declarative.Container.ListBox , GI.Gtk.Declarative.Container.MenuItem+ , GI.Gtk.Declarative.Container.Paned , GI.Gtk.Declarative.Container.Patch , GI.Gtk.Declarative.EventSource- , GI.Gtk.Declarative.Markup , GI.Gtk.Declarative.Patch , GI.Gtk.Declarative.SingleWidget , GI.Gtk.Declarative.State- build-depends: base >=4.10 && <4.12+ , GI.Gtk.Declarative.Widget+ , GI.Gtk.Declarative.Widget.Conversions+ build-depends: base >=4.10 && <5 , gi-gobject >= 2 && <3+ , gi-glib >= 2 && <3 , gi-gtk >= 3 && <4 , haskell-gi >= 0.21 && <0.22 , haskell-gi-base >= 0.21 && <0.22@@ -57,7 +70,7 @@ hs-source-dirs: bench main-is: Benchmark.hs build-depends: base >= 4 && < 5- , criterion+ , criterion >= 1.5.3.0 , gi-glib , gi-gdk , gi-gtk
src/GI/Gtk/Declarative.hs view
@@ -14,7 +14,11 @@ import GI.Gtk.Declarative.Container as Export (Container, container) import GI.Gtk.Declarative.Container.Box as Export+import GI.Gtk.Declarative.Container.ListBox as Export () import GI.Gtk.Declarative.Container.MenuItem as Export-import GI.Gtk.Declarative.Markup as Export+import GI.Gtk.Declarative.Container.Paned as Export+import GI.Gtk.Declarative.CustomWidget as Export import GI.Gtk.Declarative.Patch as Export import GI.Gtk.Declarative.SingleWidget as Export+import GI.Gtk.Declarative.Widget as Export+import GI.Gtk.Declarative.Widget.Conversions as Export ()
src/GI/Gtk/Declarative/Bin.hs view
@@ -27,9 +27,9 @@ import GI.Gtk.Declarative.Attributes.Collected import GI.Gtk.Declarative.Attributes.Internal import GI.Gtk.Declarative.EventSource-import GI.Gtk.Declarative.Markup import GI.Gtk.Declarative.Patch import GI.Gtk.Declarative.State+import GI.Gtk.Declarative.Widget -- | Supported 'Gtk.Bin's.@@ -38,6 +38,7 @@ instance BinChild Gtk.ScrolledWindow Widget where instance BinChild Gtk.ListBoxRow Widget where instance BinChild Gtk.Window Widget where+instance BinChild Gtk.ApplicationWindow Widget where instance BinChild Gtk.Dialog Widget where instance BinChild Gtk.MenuItem Widget where @@ -70,12 +71,12 @@ , Gtk.IsContainer widget , Gtk.IsBin widget , Gtk.IsWidget widget- , FromWidget (Bin widget child) event target+ , FromWidget (Bin widget child) target ) => (Gtk.ManagedPtr widget -> widget) -- ^ A bin widget constructor from the underlying gi-gtk library. -> Vector (Attribute widget event) -- ^ List of 'Attribute's. -> child event -- ^ The bin's child widget, whose type is decided by the 'BinChild' instance.- -> target -- ^ The target, whose type is decided by 'FromWidget'.+ -> target event -- ^ The target, whose type is decided by 'FromWidget'. bin ctor attrs = fromWidget . Bin ctor attrs --@@ -91,8 +92,7 @@ sc <- Gtk.widgetGetStyleContext widget' updateClasses sc mempty (collectedClasses collected) - -- TODO:- -- mapM_ (applyAfterCreated widget') props+ mapM_ (applyAfterCreated widget') attrs childState <- create child childWidget <- someStateWidget childState@@ -138,31 +138,9 @@ (<> handlers') <$> subscribe child childState cb _ -> error "Cannot subscribe to Bin events with a non-bin state tree." ------ FromWidget---+instance (a ~ b, c ~ d) => FromWidget (Bin a c) (Bin b d) where+ fromWidget = id -instance ( BinChild widget child- , Typeable widget- , Patchable child- , EventSource child- , Functor (Bin widget child)- ) =>- FromWidget (Bin widget child) event (Widget event) where+instance (BinChild widget child, Patchable child, EventSource child)+ => FromWidget (Bin widget child) Widget where fromWidget = Widget--instance a ~ () => FromWidget (Bin widget child) event (MarkupOf (Bin widget child) event a) where- fromWidget = single--instance ( BinChild widget child- , a ~ ()- , Typeable widget- , Patchable child- , EventSource child- , Functor (Bin widget child)- ) =>- FromWidget (Bin widget child) event (Markup event a) where- fromWidget = single . Widget--instance FromWidget (Bin widget child) event (Bin widget child event) where- fromWidget = id
src/GI/Gtk/Declarative/Container.hs view
@@ -1,15 +1,17 @@ {-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} -- | Implementations for common "Gtk.Container". @@ -17,6 +19,7 @@ ( Container , container , Children+ , ToChildren(..) ) where @@ -29,11 +32,12 @@ import GI.Gtk.Declarative.Attributes import GI.Gtk.Declarative.Attributes.Collected import GI.Gtk.Declarative.Attributes.Internal+import GI.Gtk.Declarative.Container.Class import GI.Gtk.Declarative.Container.Patch import GI.Gtk.Declarative.EventSource-import GI.Gtk.Declarative.Markup import GI.Gtk.Declarative.Patch import GI.Gtk.Declarative.State+import GI.Gtk.Declarative.Widget -- | Declarative version of a /container/ widget, i.e. a widget with zero -- or more child widgets. The type of 'children' is parameterized, and differs@@ -65,19 +69,14 @@ , Functor child , Gtk.IsWidget widget , Gtk.IsContainer widget- , FromWidget (Container widget (Children child)) event target+ , FromWidget (Container widget (Children child)) target+ , ToChildren widget parent child ) => (Gtk.ManagedPtr widget -> widget) -- ^ A container widget constructor from the underlying gi-gtk library.- -> Vector (Attribute widget event) -- ^ List of 'Attribute's.- -> MarkupOf child event () -- ^ The container's 'child' widgets, in a 'MarkupOf' builder.- -> target -- ^ The target, whose type is decided by 'FromWidget'.-container ctor attrs = fromWidget . Container ctor attrs . toChildren--newtype Children child event = Children { unChildren :: Vector (child event) }- deriving (Functor)--toChildren :: MarkupOf child event () -> Children child event-toChildren = Children . runMarkup+ -> Vector (Attribute widget event) -- ^ 'Attribute's.+ -> parent (child event) -- ^ The container's 'child' widgets, in a 'MarkupOf' builder.+ -> target event -- ^ The target, whose type is decided by 'FromWidget'.+container ctor attrs = fromWidget . Container ctor attrs . toChildren ctor -- -- Patchable@@ -91,8 +90,7 @@ Gtk.widgetShow widget' sc <- Gtk.widgetGetStyleContext widget' updateClasses sc mempty (collectedClasses collected)- -- TODO:- -- mapM_ (applyAfterCreated widget') props+ mapM_ (applyAfterCreated widget') attrs childStates <- forM (unChildren children) $ \child -> do childState <- create child@@ -145,21 +143,8 @@ , EventSource (Container widget children) , Functor (Container widget children) ) =>- FromWidget (Container widget children) event (Widget event) where+ FromWidget (Container widget children) Widget where fromWidget = Widget -instance a ~ () =>- FromWidget (Container widget children) event (MarkupOf (Container widget children) event a) where- fromWidget = single--instance ( a ~ ()- , Typeable widget- , Typeable children- , Patchable (Container widget children)- , EventSource (Container widget children)- , Functor (Container widget children)- ) =>- FromWidget (Container widget children) event (Markup event a) where- fromWidget = single . Widget-instance FromWidget (Container widget children) event (Container widget children event) where+instance a ~ b => FromWidget (Container a children) (Container b children) where fromWidget = id
src/GI/Gtk/Declarative/Container/Box.hs view
@@ -1,48 +1,51 @@-{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors -fno-warn-orphans #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} -- | Implementation of 'Gtk.Box' as a declarative container. module GI.Gtk.Declarative.Container.Box- ( BoxChild(..)- , boxChild+ ( BoxChild (..)+ , BoxChildProperties (..)+ , defaultBoxChildProperties ) where -import Data.Word (Word32)+import Data.Word (Word32)+import qualified GI.Gtk as Gtk +import GI.Gtk.Declarative.Container.Class import GI.Gtk.Declarative.EventSource import GI.Gtk.Declarative.Patch-import GI.Gtk.Declarative.Markup+import GI.Gtk.Declarative.Widget --- | Described a child widget to be added with 'boxAppend' to a 'Box'.+-- | Describes a child widget to be added with 'boxAppend' to a 'Box'. data BoxChild event = BoxChild+ { properties :: BoxChildProperties+ , child :: Widget event+ }+ deriving (Functor)++-- | Values used when /packing/ child widgets into boxes.+data BoxChildProperties = BoxChildProperties { expand :: Bool , fill :: Bool , padding :: Word32- , child :: Widget event }- deriving (Functor) --- | Construct a box child with the given 'boxAppend' parameters.-boxChild- :: Bool- -> Bool- -> Word32- -> Widget event- -> MarkupOf BoxChild event ()-boxChild expand fill padding child = single BoxChild {..}+-- | Defaults for 'BoxChildProperties'. Use these and override+-- specific fields.+defaultBoxChildProperties :: BoxChildProperties+defaultBoxChildProperties =+ BoxChildProperties {expand = False, fill = False, padding = 0} instance Patchable BoxChild where create = create . child@@ -50,3 +53,5 @@ instance EventSource BoxChild where subscribe BoxChild{..} = subscribe child++instance ToChildren Gtk.Box [] BoxChild
src/GI/Gtk/Declarative/Container/Class.hs view
@@ -1,7 +1,14 @@ {-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE TypeFamilies #-}++-- | Shared interfaces for containers. module GI.Gtk.Declarative.Container.Class where import qualified GI.Gtk as Gtk+import Data.Vector (Vector)+import qualified Data.Vector as Vector import Data.Int (Int32) -- | Describes supported GTK+ containers and their specialized APIs for@@ -21,3 +28,14 @@ -> Gtk.Widget -- ^ Old GTK widget to replace -> Gtk.Widget -- ^ New GTK widget to replace with -> IO ()++-- | Common collection type for child widgets, used when patching containers.+newtype Children child event = Children { unChildren :: Vector (child event) }+ deriving (Functor)++-- | Converts a specific collection type to 'Children'.+class ToChildren widget parent child | widget -> parent, widget -> child where+ toChildren :: (Gtk.ManagedPtr widget -> widget) -> parent (child event) -> Children child event++ default toChildren :: parent ~ [] => (Gtk.ManagedPtr widget -> widget) -> parent (child event) -> Children child event+ toChildren _ = Children . Vector.fromList
+ src/GI/Gtk/Declarative/Container/ListBox.hs view
@@ -0,0 +1,14 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}++module GI.Gtk.Declarative.Container.ListBox where++import qualified GI.Gtk as Gtk++import GI.Gtk.Declarative.Bin+import GI.Gtk.Declarative.Container.Class+import GI.Gtk.Declarative.Widget++instance ToChildren Gtk.ListBox [] (Bin Gtk.ListBoxRow Widget)
src/GI/Gtk/Declarative/Container/MenuItem.hs view
@@ -1,15 +1,15 @@ {-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors -fno-warn-orphans #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} module GI.Gtk.Declarative.Container.MenuItem ( MenuItem@@ -18,32 +18,41 @@ ) where -import Data.Text ( Text )+import Data.Text (Text) import Data.Typeable-import Data.Vector ( Vector )-import qualified GI.Gtk as Gtk+import Data.Vector (Vector)+import qualified GI.Gtk as Gtk import GI.Gtk.Declarative.Attributes import GI.Gtk.Declarative.Bin import GI.Gtk.Declarative.Container import GI.Gtk.Declarative.Container.Patch import GI.Gtk.Declarative.EventSource-import GI.Gtk.Declarative.Markup import GI.Gtk.Declarative.Patch import GI.Gtk.Declarative.State+import GI.Gtk.Declarative.Widget +-- | A menu item widget used for 'Gtk.Menu' children. data MenuItem event where+ -- | A single menu item in a 'Gtk.Menu'. MenuItem :: (Gtk.IsMenuItem item, BinChild item Widget, Typeable item) => Bin item Widget event -> MenuItem event+ -- | A sub menu in a 'Gtk.Menu', with a text label and the list of+ -- child menu items. SubMenu- :: Text -> (Container Gtk.Menu (Children MenuItem) event) -> MenuItem event+ :: Text -> Container Gtk.Menu (Children MenuItem) event -> MenuItem event instance Functor MenuItem where- fmap f (MenuItem item) = MenuItem (fmap f item)- fmap f (SubMenu label subMenu')= SubMenu label (fmap f subMenu')+ fmap f (MenuItem item) = MenuItem (fmap f item)+ fmap f (SubMenu label subMenu')=SubMenu label (fmap f subMenu') +instance ToChildren Gtk.Menu [] MenuItem++instance ToChildren Gtk.MenuBar [] MenuItem++-- | Construct a single menu item for a 'Gtk.Menu'. menuItem :: ( Gtk.IsMenuItem item , Typeable event@@ -56,15 +65,17 @@ => (Gtk.ManagedPtr item -> item) -> Vector (Attribute item event) -> Widget event- -> MarkupOf MenuItem event ()-menuItem item attrs = single . MenuItem . Bin item attrs+ -> MenuItem event+menuItem item attrs = MenuItem . Bin item attrs +-- | Construct a sub menu for a 'Gtk.Menu', wit a text label and the+-- child menu items. subMenu :: (Typeable event) => Text- -> MarkupOf MenuItem event ()- -> MarkupOf MenuItem event ()-subMenu label = single . SubMenu label . container Gtk.Menu mempty+ -> [MenuItem event]+ -> MenuItem event+subMenu label = SubMenu label . container Gtk.Menu mempty newSubMenuItem :: Text -> IO SomeState -> IO SomeState newSubMenuItem label createSubMenu = do
+ src/GI/Gtk/Declarative/Container/Paned.hs view
@@ -0,0 +1,104 @@+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors -fno-warn-orphans #-}++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}++-- | Implementation of 'Gtk.Paned' as a declarative container.+module GI.Gtk.Declarative.Container.Paned+ ( Pane+ , PaneProperties (..)+ , defaultPaneProperties+ , pane+ , paned+ )+where++import Data.Coerce (coerce)+import Data.Typeable+import Data.Vector (Vector)+import qualified Data.Vector as Vector+import GHC.Ptr (nullPtr)+import qualified GI.GLib as GLib+import qualified GI.Gtk as Gtk++import GI.Gtk.Declarative.Attributes+import GI.Gtk.Declarative.Container+import GI.Gtk.Declarative.Container.Class+import GI.Gtk.Declarative.EventSource+import GI.Gtk.Declarative.Patch+import GI.Gtk.Declarative.Widget++-- | Describes a pane to be packed with+-- 'Gtk.panePack1'/'Gtk.panePack2' in a 'Gtk.Paned'.+data Pane event = Pane+ { paneProperties :: PaneProperties+ , paneChild :: Widget event+ }+ deriving (Functor)++-- | Values used when packing a pane into a 'Gtk.Paned'.+data PaneProperties = PaneProperties+ { resize :: Bool+ , shrink :: Bool+ }++-- | Defaults for 'PaneProperties'. Use these and override specific+-- fields.+defaultPaneProperties :: PaneProperties+defaultPaneProperties = PaneProperties {resize = False, shrink = True}++-- | Construct a pane to be packed with+-- 'Gtk.panePack1'/'Gtk.panePack2' in a 'Gtk.Paned'.+pane :: PaneProperties -> Widget event -> Pane event+pane paneProperties paneChild = Pane {..}++instance Patchable Pane where+ create = create . paneChild+ patch s b1 b2 = patch s (paneChild b1) (paneChild b2)++instance EventSource Pane where+ subscribe Pane{..} = subscribe paneChild++-- | Construct a 'Gtk.Paned' based on attributes and two child 'Pane's.+paned :: Typeable event => Vector (Attribute Gtk.Paned event) -> Pane event -> Pane event -> Widget event+paned attrs p1 p2 = container Gtk.Paned attrs (Panes p1 p2)++data Panes child = Panes child child+ deriving (Functor)++instance IsContainer Gtk.Paned Pane where+ appendChild paned' Pane{paneProperties = PaneProperties{resize, shrink}} widget' = do+ c1 <- Gtk.panedGetChild1 paned'+ c2 <- Gtk.panedGetChild2 paned'+ case (c1, c2) of+ (Nothing, Nothing) -> Gtk.panedPack1 paned' widget' (coerce resize) (coerce shrink)+ (Just _, Nothing) -> Gtk.panedPack2 paned' widget' (coerce resize) (coerce shrink)+ _ -> GLib.logDefaultHandler+ (Just "gi-gtk-declarative")+ [GLib.LogLevelFlagsLevelWarning]+ (Just "appendChild: The `GI.Gtk.Paned` widget can only fit 2 panes. Additional children will be ignored.")+ nullPtr+ replaceChild paned' Pane{paneProperties = PaneProperties{resize, shrink}} i old new = do+ Gtk.widgetDestroy old+ case i of+ 0 -> Gtk.panedPack1 paned' new (coerce resize) (coerce shrink)+ 1 -> Gtk.panedPack2 paned' new (coerce resize) (coerce shrink)+ _ -> GLib.logDefaultHandler+ (Just "gi-gtk-declarative")+ [GLib.LogLevelFlagsLevelWarning]+ (Just "replaceChild: The `GI.Gtk.Paned` widget can only fit 2 panes. Additional children will be ignored.")+ nullPtr++instance ToChildren Gtk.Paned Panes Pane where+ toChildren _ (Panes p1 p2) = Children (Vector.fromList [p1, p2])
src/GI/Gtk/Declarative/Container/Patch.hs view
@@ -6,9 +6,10 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}@@ -28,9 +29,9 @@ import GI.Gtk.Declarative.Bin import GI.Gtk.Declarative.Container.Box import GI.Gtk.Declarative.Container.Class-import GI.Gtk.Declarative.Markup import GI.Gtk.Declarative.Patch import GI.Gtk.Declarative.State+import GI.Gtk.Declarative.Widget -- | Patch all children in a container. This does not feature any ID checking, -- as seen in React, so reordering children in a container can produce many@@ -123,7 +124,7 @@ Gtk.listBoxInsert box new i instance IsContainer Gtk.Box BoxChild where- appendChild box BoxChild {..} widget' =+ appendChild box BoxChild {properties = BoxChildProperties {expand, fill, padding}} widget' = Gtk.boxPackStart box widget' expand fill padding replaceChild box boxChild' i old new = do Gtk.widgetDestroy old
+ src/GI/Gtk/Declarative/CustomWidget.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE DeriveFunctor #-}++-- | While you can instantiate 'Patchable' and 'EventSource' for your+-- own data types, it's a bit complicated. The 'CustomWidget' data+-- type takes care of some lower-level detail, so that you can focus+-- on the custom behavior of your widget. You still need to think+-- about and implement a patching function, but in an easier way.+module GI.Gtk.Declarative.CustomWidget+ ( CustomPatch(..)+ , CustomWidget(..)+ )+where++import qualified GI.Gtk as Gtk+import GI.Gtk.Declarative.EventSource+import GI.Gtk.Declarative.Patch+import GI.Gtk.Declarative.State++-- | Similar to 'Patch', describing a possible action to perform on a+-- 'Gtk.Widget', decided by 'customPatch'.+data CustomPatch widget customData+ = CustomReplace+ | CustomModify (widget -> IO SomeState)+ | CustomKeep++-- | A custom widget specification, with all functions needed to+-- instantiate 'Patchable' and 'EventSource'. A custom widget is based+-- on a top 'widget', can use 'customData' as a way of passing+-- parameters, and emits events of type 'event'.+data CustomWidget widget customData event =+ CustomWidget+ { customWidget :: Gtk.ManagedPtr widget -> widget+ -- ^ The widget constructor+ , customCreate :: customData -> IO SomeState+ -- ^ Action that creates the initial widget+ , customPatch :: SomeState -> customData -> customData -> CustomPatch widget customData+ -- ^ Patch function, calculating a 'CustomPatch' based on the state,+ -- old custom data, and new custom data.+ , customSubscribe :: customData -> widget -> (event -> IO ()) -> IO Subscription+ -- ^ Action that creates an event subscription for the custom widget+ , customData :: customData+ -- ^ The custom data (e.g. parameters) of the custom widget+ } deriving (Functor)++instance Gtk.IsWidget widget => Patchable (CustomWidget widget customData) where+ create custom = customCreate custom (customData custom)+ patch state' old new =+ case customPatch old state' (customData old) (customData new) of+ CustomReplace -> Replace (customCreate new (customData new))+ CustomModify f -> Modify (f =<< Gtk.unsafeCastTo (customWidget new) =<< someStateWidget state')+ CustomKeep -> Keep++instance Gtk.GObject widget => EventSource (CustomWidget widget customData) where+ subscribe custom state' cb = do+ w' <- Gtk.unsafeCastTo (customWidget custom) =<< someStateWidget state'+ customSubscribe custom (customData custom) w' cb
− src/GI/Gtk/Declarative/Markup.hs
@@ -1,105 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeOperators #-}---- | 'MarkupOf' is the common builder type for the (declarative) GTK+ markup--- language, supporting do syntax. 'MarkupOf' wraps many widgets, usually of--- type 'Widget', but they can be of other types.------ A 'Widget' value can wrap any 'Patchable' widget, hiding the underlying--- widget type, such that you can embed heterogeneous collections of widgets in--- containers.-module GI.Gtk.Declarative.Markup- ( Widget(..)- -- * Markup- , MarkupOf- , Markup- , single- , multiple- , runMarkup- -- * Widget to Markup conversion- , FromWidget(..)- )-where--import Control.Monad.Writer-import Data.Typeable-import Data.Vector (Vector)--import GI.Gtk.Declarative.EventSource-import GI.Gtk.Declarative.Patch---- | A 'Widget' value wraps a 'Patchable' and 'EventSource' widget, providing--- a constrained equivalent of a 'Dynamic' value. It is used to support--- heterogeneous containers of widgets, and to support equality--- checks on different types of widgets when calculating patches.-data Widget event where- Widget- :: ( Typeable widget- , Patchable widget- , Functor widget- , EventSource widget- )- => widget event- -> Widget event--instance Functor Widget where- fmap f (Widget w) = Widget (fmap f w)---- | 'Widget' is itself patchable, by delegating to the underlying--- widget instances.-instance Patchable Widget where- create (Widget w) = create w- patch s (Widget (w1 :: t1 e1)) (Widget (w2 :: t2 e2)) =- case eqT @t1 @t2 of- Just Refl -> patch s w1 w2- _ -> Replace (create w2)--instance EventSource Widget where- subscribe (Widget w) = subscribe w---- * Markup---- | The declarative markup builder, primarily for using its 'Monad' instance--- and do notation to construct adjacent widgets in containers.------ It is parameterized with 'widget' and 'event', such that containers can--- restrict the type of their children to other types than 'Widget'.------ Note that the return type, 'a', is not used in this library. It's a more a--- technical necessity to have the 'Monad' instance. You can still use it if--- you need to return a value from a markup function, though.-newtype MarkupOf widget event a =- MarkupOf (Writer (Vector (widget event)) a)- deriving (Functor, Applicative, Monad)---- | Run a 'MarkupOf' builder and get its widgets.-runMarkup :: MarkupOf widget event () -> Vector (widget event)-runMarkup (MarkupOf w) = execWriter w---- | Handy type alias for the common case of markup containing 'Widget's.-type Markup event a = MarkupOf Widget event a---- | Construct markup from a single widget.-single :: widget event -> MarkupOf widget event ()-single = MarkupOf . tell . pure---- | Construct markup from multiple widgets.-multiple :: Vector (widget event) -> MarkupOf widget event ()-multiple = MarkupOf . tell---- | Convert a widget to a target type. This is deliberately unconstrained in--- it's types, and is used by smart constructors to implement return type--- polymorphism, so that a smart contructor can return either a 'Widget', or--- some specifically typed 'MarkupOf', depending on the context in which it's--- used.-class FromWidget widget event target | target -> event where- fromWidget :: (Typeable widget, Typeable event) => widget event -> target--instance FromWidget Widget event (Widget event) where- fromWidget = id
src/GI/Gtk/Declarative/SingleWidget.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -8,6 +7,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} -- | A declarative representation of 'Gtk.Widget' in GTK without children.@@ -25,9 +25,9 @@ import GI.Gtk.Declarative.Attributes.Collected import GI.Gtk.Declarative.Attributes.Internal import GI.Gtk.Declarative.EventSource-import GI.Gtk.Declarative.Markup import GI.Gtk.Declarative.Patch import GI.Gtk.Declarative.State+import GI.Gtk.Declarative.Widget -- | Declarative version of a /leaf/ widget, i.e. a widget without any children. data SingleWidget widget event where@@ -48,8 +48,7 @@ Gtk.widgetShow widget' sc <- Gtk.widgetGetStyleContext widget' updateClasses sc mempty (collectedClasses collected)- -- TODO:- -- mapM_ (applyAfterCreated widget') props+ mapM_ (applyAfterCreated widget') attrs return (SomeState (StateTreeWidget (StateTreeNode widget' sc collected ()))) patch (SomeState (st :: StateTree stateType w child event cs))@@ -67,22 +66,15 @@ _ -> Replace (create (SingleWidget ctor newAttributes)) instance EventSource (SingleWidget widget) where- subscribe (SingleWidget (_ :: Gtk.ManagedPtr w1 -> w1) props) (SomeState (st :: StateTree stateType w2 child event cs)) cb = do+ subscribe (SingleWidget (_ :: Gtk.ManagedPtr w1 -> w1) props) (SomeState (st :: StateTree stateType w2 child event cs)) cb = case (st, eqT @w1 @w2) of (StateTreeWidget top, Just Refl) -> foldMap (addSignalHandler cb (stateTreeWidget top)) props _ -> fail "" -instance (Typeable widget, Functor (SingleWidget widget))- => FromWidget (SingleWidget widget) event (Widget event) where- fromWidget = Widget--instance FromWidget (SingleWidget widget) event (MarkupOf (SingleWidget widget) event ()) where- fromWidget = single--instance (Typeable widget, Functor (SingleWidget widget))- => FromWidget (SingleWidget widget) event (Markup event ()) where- fromWidget = single . Widget+-- instance (Typeable widget, Functor (SingleWidget widget))+-- => FromWidget (SingleWidget widget) Widget where+-- fromWidget = Widget -- | Construct a /leaf/ widget, i.e. one without any children. widget@@ -90,9 +82,9 @@ , Typeable event , Functor (Attribute widget) , Gtk.IsWidget widget- , FromWidget (SingleWidget widget) event target+ , FromWidget (SingleWidget widget) target ) => (Gtk.ManagedPtr widget -> widget) -- ^ A widget constructor from the underlying gi-gtk library. -> Vector (Attribute widget event) -- ^ List of 'Attribute's.- -> target -- ^ The target, whose type is decided by 'FromWidget'.+ -> target event -- ^ The target, whose type is decided by 'FromWidget'. widget ctor = fromWidget . SingleWidget ctor
+ src/GI/Gtk/Declarative/Widget.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}++-- | A 'Widget' value can wrap any 'Patchable' widget, hiding the underlying+-- widget type, such that you can embed heterogeneous collections of widgets in+-- containers.+module GI.Gtk.Declarative.Widget+ ( Widget(..)+ -- * Widget to Markup conversion+ , FromWidget(..)+ )+where++import Data.Typeable++import GI.Gtk.Declarative.EventSource+import GI.Gtk.Declarative.Patch++-- | A 'Widget' value wraps a 'Patchable' and 'EventSource' widget, providing+-- a constrained equivalent of a 'Dynamic' value. It is used to support+-- heterogeneous containers of widgets, and to support equality+-- checks on different types of widgets when calculating patches.+data Widget event where+ Widget+ :: ( Typeable widget+ , Patchable widget+ , Functor widget+ , EventSource widget+ )+ => widget event+ -> Widget event++instance Functor Widget where+ fmap f (Widget w) = Widget (fmap f w)++-- | 'Widget' is itself patchable, by delegating to the underlying+-- widget instances.+instance Patchable Widget where+ create (Widget w) = create w+ patch s (Widget (w1 :: t1 e1)) (Widget (w2 :: t2 e2)) =+ case eqT @t1 @t2 of+ Just Refl -> patch s w1 w2+ _ -> Replace (create w2)++instance EventSource Widget where+ subscribe (Widget w) = subscribe w++-- | Convert a widget to a target type. This is deliberately unconstrained in+-- it's types, and is used by smart constructors to implement return type+-- polymorphism, so that a smart contructor can return either a 'Widget', or+-- some specifically typed widget, depending on the context in which it's+-- used.+class FromWidget widget target where+ fromWidget :: (Typeable widget, Typeable event) => widget event -> target event++instance (Patchable (parent child), Functor (parent child), EventSource (parent child))+ => FromWidget (parent child) Widget where+ fromWidget = Widget
+ src/GI/Gtk/Declarative/Widget/Conversions.hs view
@@ -0,0 +1,18 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}++-- | Orphan instances for automatically converting between different+-- widget types. As these use 'UndecidableInstances', they are kept+-- here in solitary confinement.+module GI.Gtk.Declarative.Widget.Conversions where++import GI.Gtk.Declarative.Container.Box+import GI.Gtk.Declarative.Widget++-- | Any widget that can be converted to a 'Widget' can be wrapped+-- as a 'BoxChild' with the default properties.+instance FromWidget widget Widget => FromWidget widget BoxChild where+ fromWidget = BoxChild defaultBoxChildProperties . fromWidget