packages feed

gi-gtk-declarative 0.1.0 → 0.2.0

raw patch · 22 files changed

+953/−459 lines, 22 filesdep +criteriondep +gi-gdkdep +gi-glibdep ~basedep ~gi-gtksetup-changedPVP ok

version bump matches the API change (PVP)

Dependencies added: criterion, gi-gdk, gi-glib, gi-gtk-declarative, random, vector

Dependency ranges changed: base, gi-gtk

API changes (from Hackage documentation)

- GI.Gtk.Declarative.Attributes: class ToGtkCallback userCallback where {
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.ImpureCallback (GHC.Types.IO ()) widget)
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.ImpureCallback (x -> GHC.Types.IO ()) widget)
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.ImpureCallback (x -> y -> GHC.Types.IO ()) widget)
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.PureCallback (GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.PureCallback (x -> GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.PureCallback (x -> y -> GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: instance GI.Gtk.Declarative.Attributes.ToGtkCallback (GI.Gtk.Declarative.Attributes.ImpureCallback (GHC.Types.IO ()) widget)
- GI.Gtk.Declarative.Attributes: instance GI.Gtk.Declarative.Attributes.ToGtkCallback (GI.Gtk.Declarative.Attributes.PureCallback (GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: instance GI.Gtk.Declarative.Attributes.ToGtkCallback (GI.Gtk.Declarative.Attributes.PureCallback (x -> GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: instance GI.Gtk.Declarative.Attributes.ToGtkCallback (GI.Gtk.Declarative.Attributes.PureCallback (x -> y -> GHC.Types.IO ()))
- GI.Gtk.Declarative.Attributes: toGtkCallback :: ToGtkCallback userCallback => userCallback event -> (event -> IO ()) -> CustomGtkCallback userCallback
- GI.Gtk.Declarative.Attributes: type family CustomGtkCallback userCallback :: *;
- GI.Gtk.Declarative.Attributes: }
- GI.Gtk.Declarative.Attributes.Internal: addClass :: MonadIO m => StyleContext -> Attribute widget event -> m ()
- GI.Gtk.Declarative.Attributes.Internal: extractAttrConstructOps :: Attribute widget event -> [AttrOp widget 'AttrConstruct]
- GI.Gtk.Declarative.Attributes.Internal: extractAttrSetOps :: Attribute widget event -> [AttrOp widget 'AttrSet]
- GI.Gtk.Declarative.Attributes.Internal: removeClass :: MonadIO m => StyleContext -> Attribute widget event -> m ()
- GI.Gtk.Declarative.CSS: addClasses :: IsWidget w => w -> ClassSet -> IO ()
- GI.Gtk.Declarative.CSS: replaceClasses :: IsWidget w => w -> ClassSet -> ClassSet -> IO ()
- GI.Gtk.Declarative.CSS: type ClassSet = HashSet Text
- GI.Gtk.Declarative.Container: instance (GI.Gtk.Declarative.Patch.Patchable child, GI.Gtk.Declarative.Container.Patch.IsContainer container child) => GI.Gtk.Declarative.Patch.Patchable (GI.Gtk.Declarative.Container.Container container (GI.Gtk.Declarative.Container.Children child))
- GI.Gtk.Declarative.Container.Patch: instance GI.Gtk.Declarative.Container.Patch.IsContainer GI.Gtk.Objects.Box.Box GI.Gtk.Declarative.Container.Box.BoxChild
- GI.Gtk.Declarative.Container.Patch: instance GI.Gtk.Declarative.Container.Patch.IsContainer GI.Gtk.Objects.ListBox.ListBox (GI.Gtk.Declarative.Bin.Bin GI.Gtk.Objects.ListBoxRow.ListBoxRow GI.Gtk.Declarative.Markup.Widget)
+ GI.Gtk.Declarative: BoxChild :: Bool -> Bool -> Word32 -> Widget event -> BoxChild event
+ GI.Gtk.Declarative: Keep :: Patch
+ GI.Gtk.Declarative: Modify :: (IO SomeState) -> Patch
+ GI.Gtk.Declarative: Replace :: (IO SomeState) -> Patch
+ GI.Gtk.Declarative: [:=] :: (AttrOpAllowed 'AttrConstruct info widget, AttrOpAllowed 'AttrSet info widget, AttrGetC info widget attr getValue, AttrSetTypeConstraint info setValue, KnownSymbol attr, Typeable attr, Eq setValue, Typeable setValue) => AttrLabelProxy (attr :: Symbol) -> setValue -> Attribute widget event
+ GI.Gtk.Declarative: [AfterCreated] :: (widget -> IO ()) -> Attribute widget event
+ GI.Gtk.Declarative: [Classes] :: IsWidget widget => ClassSet -> Attribute widget event
+ GI.Gtk.Declarative: [EventHandlerFunction] :: (a -> EventHandler b w p e) -> EventHandler (a -> b) w p e
+ GI.Gtk.Declarative: [ImpureEventHandler] :: (w -> EventHandlerReturn IO ret e) -> EventHandler (IO ret) w Impure e
+ GI.Gtk.Declarative: [OnSignalImpure] :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Impure) => SignalProxy widget info -> EventHandler gtkCallback widget Impure event -> Attribute widget event
+ GI.Gtk.Declarative: [OnSignalPure] :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Pure) => SignalProxy widget info -> EventHandler gtkCallback widget Pure event -> Attribute widget event
+ GI.Gtk.Declarative: [PureEventHandler] :: EventHandlerReturn Identity ret e -> EventHandler (IO ret) w Pure e
+ GI.Gtk.Declarative: [Widget] :: (Typeable widget, Patchable widget, Functor widget, EventSource widget) => widget event -> Widget event
+ GI.Gtk.Declarative: [child] :: BoxChild event -> Widget event
+ GI.Gtk.Declarative: [expand] :: BoxChild event -> Bool
+ GI.Gtk.Declarative: [fill] :: BoxChild event -> Bool
+ GI.Gtk.Declarative: [padding] :: BoxChild event -> Word32
+ GI.Gtk.Declarative: afterCreated :: (widget -> IO ()) -> Attribute widget event
+ GI.Gtk.Declarative: bin :: (Patchable (Bin widget child), Typeable widget, Typeable child, Typeable event, Functor child, IsContainer widget, IsBin widget, IsWidget widget, FromWidget (Bin widget child) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> child event -> target
+ GI.Gtk.Declarative: boxChild :: Bool -> Bool -> Word32 -> Widget event -> MarkupOf BoxChild event ()
+ GI.Gtk.Declarative: class FromWidget widget event target | target -> event
+ GI.Gtk.Declarative: class Patchable widget
+ GI.Gtk.Declarative: classes :: IsWidget widget => [Text] -> Attribute widget event
+ GI.Gtk.Declarative: container :: (Patchable (Container widget (Children child)), Typeable widget, Typeable child, Typeable event, Functor child, IsWidget widget, IsContainer widget, FromWidget (Container widget (Children child)) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> MarkupOf child event () -> target
+ GI.Gtk.Declarative: create :: Patchable widget => widget e -> IO SomeState
+ GI.Gtk.Declarative: data Attribute widget event
+ GI.Gtk.Declarative: data Bin widget child event
+ GI.Gtk.Declarative: data BoxChild event
+ GI.Gtk.Declarative: data Container widget children event
+ GI.Gtk.Declarative: data EventHandler gtkEventHandler widget (purity :: Purity) event
+ GI.Gtk.Declarative: data MarkupOf widget event a
+ GI.Gtk.Declarative: data MenuItem event
+ GI.Gtk.Declarative: data Patch
+ GI.Gtk.Declarative: data SingleWidget widget event
+ GI.Gtk.Declarative: data Widget event
+ GI.Gtk.Declarative: fromWidget :: (FromWidget widget event target, Typeable widget, Typeable event) => widget event -> target
+ GI.Gtk.Declarative: menuItem :: (IsMenuItem item, Typeable event, BinChild item Widget, Typeable item, IsContainer item, IsBin item, IsWidget item) => (ManagedPtr item -> item) -> Vector (Attribute item event) -> Widget event -> MarkupOf MenuItem event ()
+ GI.Gtk.Declarative: multiple :: Vector (widget event) -> MarkupOf widget event ()
+ GI.Gtk.Declarative: on :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Pure, ToEventHandler gtkCallback widget Pure, userEventHandler ~ UserEventHandler gtkCallback widget Pure event) => SignalProxy widget info -> userEventHandler -> Attribute widget event
+ GI.Gtk.Declarative: onM :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Impure, ToEventHandler gtkCallback widget Impure, userEventHandler ~ UserEventHandler gtkCallback widget Impure event) => SignalProxy widget info -> userEventHandler -> Attribute widget event
+ GI.Gtk.Declarative: patch :: Patchable widget => SomeState -> widget e1 -> widget e2 -> Patch
+ GI.Gtk.Declarative: runMarkup :: MarkupOf widget event () -> Vector (widget event)
+ GI.Gtk.Declarative: single :: widget event -> MarkupOf widget event ()
+ GI.Gtk.Declarative: subMenu :: (Typeable event) => Text -> MarkupOf MenuItem event () -> MarkupOf MenuItem event ()
+ GI.Gtk.Declarative: type ClassSet = HashSet Text
+ GI.Gtk.Declarative: type Markup event a = MarkupOf Widget event a
+ GI.Gtk.Declarative: widget :: (Typeable widget, Typeable event, Functor (Attribute widget), IsWidget widget, FromWidget (SingleWidget widget) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> target
+ GI.Gtk.Declarative.Attributes: [AfterCreated] :: (widget -> IO ()) -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: [EventHandlerFunction] :: (a -> EventHandler b w p e) -> EventHandler (a -> b) w p e
+ GI.Gtk.Declarative.Attributes: [ImpureEventHandler] :: (w -> EventHandlerReturn IO ret e) -> EventHandler (IO ret) w Impure e
+ GI.Gtk.Declarative.Attributes: [PureEventHandler] :: EventHandlerReturn Identity ret e -> EventHandler (IO ret) w Pure e
+ GI.Gtk.Declarative.Attributes: afterCreated :: (widget -> IO ()) -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: data EventHandler gtkEventHandler widget (purity :: Purity) event
+ GI.Gtk.Declarative.Attributes: type ClassSet = HashSet Text
+ GI.Gtk.Declarative.Attributes.Collected: Collected :: ClassSet -> CollectedProperties widget -> Collected widget event
+ GI.Gtk.Declarative.Attributes.Collected: [collectedClasses] :: Collected widget event -> ClassSet
+ GI.Gtk.Declarative.Attributes.Collected: [collectedProperties] :: Collected widget event -> CollectedProperties widget
+ GI.Gtk.Declarative.Attributes.Collected: collectAttributes :: Vector (Attribute widget event) -> Collected widget event
+ GI.Gtk.Declarative.Attributes.Collected: constructProperties :: Collected widget event -> [AttrOp widget 'AttrConstruct]
+ GI.Gtk.Declarative.Attributes.Collected: data Collected widget event
+ GI.Gtk.Declarative.Attributes.Collected: instance GHC.Base.Monoid (GI.Gtk.Declarative.Attributes.Collected.Collected widget event)
+ GI.Gtk.Declarative.Attributes.Collected: instance GHC.Base.Semigroup (GI.Gtk.Declarative.Attributes.Collected.Collected widget event)
+ GI.Gtk.Declarative.Attributes.Collected: type CollectedProperties widget = HashMap Text (CollectedProperty widget)
+ GI.Gtk.Declarative.Attributes.Collected: updateClasses :: StyleContext -> ClassSet -> ClassSet -> IO ()
+ GI.Gtk.Declarative.Attributes.Collected: updateProperties :: widget -> CollectedProperties widget -> CollectedProperties widget -> IO ()
+ GI.Gtk.Declarative.Attributes.Internal: applyAfterCreated :: widget -> Attribute widget event -> IO ()
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: class ToGtkCallback gtkCallback purity
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: instance GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback (GHC.Types.IO ()) 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Impure
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: instance GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback (GHC.Types.IO ()) 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Pure
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: instance GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback (GHC.Types.IO GHC.Types.Bool) 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Impure
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: instance GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback (GHC.Types.IO GHC.Types.Bool) 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Pure
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: instance GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback y purity => GI.Gtk.Declarative.Attributes.Internal.Conversions.ToGtkCallback (x -> y) purity
+ GI.Gtk.Declarative.Attributes.Internal.Conversions: toGtkCallback :: ToGtkCallback gtkCallback purity => EventHandler gtkCallback widget purity event -> widget -> (event -> IO ()) -> gtkCallback
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: Impure :: Purity
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: Pure :: Purity
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: [EventHandlerFunction] :: (a -> EventHandler b w p e) -> EventHandler (a -> b) w p e
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: [ImpureEventHandler] :: (w -> EventHandlerReturn IO ret e) -> EventHandler (IO ret) w Impure e
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: [OnlyEvent] :: m e -> EventHandlerReturn m () e
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: [PureEventHandler] :: EventHandlerReturn Identity ret e -> EventHandler (IO ret) w Pure e
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: [ReturnAndEvent] :: m (Bool, e) -> EventHandlerReturn m Bool e
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: class ToEventHandler gtkEventHandler widget purity
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: data EventHandler gtkEventHandler widget (purity :: Purity) event
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: data EventHandlerReturn m gtkReturn event
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: data Purity
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GHC.Base.Functor (GI.Gtk.Declarative.Attributes.Internal.EventHandler.EventHandler gtkEventHandler widget purity)
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GHC.Base.Functor m => GHC.Base.Functor (GI.Gtk.Declarative.Attributes.Internal.EventHandler.EventHandlerReturn m gtkEventHandler)
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler (GHC.Types.IO ()) widget 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Impure
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler (GHC.Types.IO ()) widget 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Pure
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler (GHC.Types.IO GHC.Types.Bool) widget 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Impure
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler (GHC.Types.IO GHC.Types.Bool) widget 'GI.Gtk.Declarative.Attributes.Internal.EventHandler.Pure
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: instance GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler b widget purity => GI.Gtk.Declarative.Attributes.Internal.EventHandler.ToEventHandler (a -> b) widget purity
+ GI.Gtk.Declarative.Attributes.Internal.EventHandler: toEventHandler :: ToEventHandler gtkEventHandler widget purity => UserEventHandler gtkEventHandler widget purity event -> EventHandler gtkEventHandler widget purity event
+ GI.Gtk.Declarative.Bin: [Bin] :: (Typeable widget, IsContainer widget, IsBin widget, IsWidget widget, Functor child) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> child event -> Bin widget child event
+ GI.Gtk.Declarative.Bin: class BinChild bin (child :: * -> *) | bin -> child
+ GI.Gtk.Declarative.Bin: instance GI.Gtk.Declarative.Bin.BinChild GI.Gtk.Objects.Dialog.Dialog GI.Gtk.Declarative.Markup.Widget
+ GI.Gtk.Declarative.Bin: instance GI.Gtk.Declarative.Bin.BinChild GI.Gtk.Objects.MenuItem.MenuItem GI.Gtk.Declarative.Markup.Widget
+ GI.Gtk.Declarative.Bin: instance GI.Gtk.Declarative.Bin.BinChild GI.Gtk.Objects.Window.Window GI.Gtk.Declarative.Markup.Widget
+ GI.Gtk.Declarative.Bin: instance GI.Gtk.Declarative.Markup.FromWidget (GI.Gtk.Declarative.Bin.Bin widget child) event (GI.Gtk.Declarative.Bin.Bin widget child event)
+ GI.Gtk.Declarative.Container: data Children child event
+ GI.Gtk.Declarative.Container: instance (GI.Gtk.Declarative.Patch.Patchable child, Data.Typeable.Internal.Typeable child, GI.Gtk.Declarative.Container.Class.IsContainer container child) => GI.Gtk.Declarative.Patch.Patchable (GI.Gtk.Declarative.Container.Container container (GI.Gtk.Declarative.Container.Children child))
+ GI.Gtk.Declarative.Container: instance GI.Gtk.Declarative.Markup.FromWidget (GI.Gtk.Declarative.Container.Container widget children) event (GI.Gtk.Declarative.Container.Container widget children event)
+ GI.Gtk.Declarative.Container.Class: appendChild :: IsContainer container child => container -> child event -> Widget -> IO ()
+ GI.Gtk.Declarative.Container.Class: class IsContainer container child | container -> child
+ GI.Gtk.Declarative.Container.Class: replaceChild :: IsContainer container child => container -> child event -> Int32 -> Widget -> Widget -> IO ()
+ GI.Gtk.Declarative.Container.MenuItem: data MenuItem event
+ GI.Gtk.Declarative.Container.MenuItem: instance GHC.Base.Functor GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: instance GI.Gtk.Declarative.Container.Class.IsContainer GI.Gtk.Objects.Menu.Menu GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: instance GI.Gtk.Declarative.Container.Class.IsContainer GI.Gtk.Objects.MenuBar.MenuBar GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: instance GI.Gtk.Declarative.Container.Class.IsContainer GI.Gtk.Objects.MenuShell.MenuShell GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: instance GI.Gtk.Declarative.EventSource.EventSource GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: instance GI.Gtk.Declarative.Patch.Patchable GI.Gtk.Declarative.Container.MenuItem.MenuItem
+ GI.Gtk.Declarative.Container.MenuItem: menuItem :: (IsMenuItem item, Typeable event, BinChild item Widget, Typeable item, IsContainer item, IsBin item, IsWidget item) => (ManagedPtr item -> item) -> Vector (Attribute item event) -> Widget event -> MarkupOf MenuItem event ()
+ GI.Gtk.Declarative.Container.MenuItem: subMenu :: (Typeable event) => Text -> MarkupOf MenuItem event () -> MarkupOf MenuItem event ()
+ GI.Gtk.Declarative.Container.Patch: instance GI.Gtk.Declarative.Container.Class.IsContainer GI.Gtk.Objects.Box.Box GI.Gtk.Declarative.Container.Box.BoxChild
+ GI.Gtk.Declarative.Container.Patch: instance GI.Gtk.Declarative.Container.Class.IsContainer GI.Gtk.Objects.ListBox.ListBox (GI.Gtk.Declarative.Bin.Bin GI.Gtk.Objects.ListBoxRow.ListBoxRow GI.Gtk.Declarative.Markup.Widget)
+ GI.Gtk.Declarative.State: BinState :: StateType
+ GI.Gtk.Declarative.State: ContainerState :: StateType
+ GI.Gtk.Declarative.State: StateTreeNode :: !widget -> !StyleContext -> !(Collected widget event) -> customState -> StateTreeNode widget event customState
+ GI.Gtk.Declarative.State: WidgetState :: StateType
+ GI.Gtk.Declarative.State: [SomeState] :: (IsWidget widget, Typeable widget, Typeable customState) => StateTree stateType widget child event customState -> SomeState
+ GI.Gtk.Declarative.State: [StateTreeBin] :: !(StateTreeNode widget event customState) -> SomeState -> StateTree 'BinState widget child event customState
+ GI.Gtk.Declarative.State: [StateTreeContainer] :: (IsContainer widget, IsContainer widget child) => !(StateTreeNode widget event customState) -> Vector SomeState -> StateTree 'ContainerState widget child event customState
+ GI.Gtk.Declarative.State: [StateTreeWidget] :: !(StateTreeNode widget event customState) -> StateTree 'WidgetState widget child event customState
+ GI.Gtk.Declarative.State: [stateTreeCollectedAttributes] :: StateTreeNode widget event customState -> !(Collected widget event)
+ GI.Gtk.Declarative.State: [stateTreeCustomState] :: StateTreeNode widget event customState -> customState
+ GI.Gtk.Declarative.State: [stateTreeStyleContext] :: StateTreeNode widget event customState -> !StyleContext
+ GI.Gtk.Declarative.State: [stateTreeWidget] :: StateTreeNode widget event customState -> !widget
+ GI.Gtk.Declarative.State: data SomeState
+ GI.Gtk.Declarative.State: data StateTree (stateType :: StateType) widget child event customState
+ GI.Gtk.Declarative.State: data StateTreeNode widget event customState
+ GI.Gtk.Declarative.State: data StateType
+ GI.Gtk.Declarative.State: someStateWidget :: SomeState -> IO Widget
+ GI.Gtk.Declarative.State: stateTreeNode :: StateTree stateType widget child event customState -> StateTreeNode widget event customState
+ GI.Gtk.Declarative.State: stateTreeNodeWidget :: StateTree stateType widget child event customState -> widget
- GI.Gtk.Declarative.Attributes: [:=] :: (AttrOpAllowed 'AttrConstruct info widget, AttrOpAllowed 'AttrSet info widget, AttrGetC info widget attr getValue, AttrSetTypeConstraint info setValue, KnownSymbol attr, Typeable attr) => AttrLabelProxy (attr :: Symbol) -> setValue -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: [:=] :: (AttrOpAllowed 'AttrConstruct info widget, AttrOpAllowed 'AttrSet info widget, AttrGetC info widget attr getValue, AttrSetTypeConstraint info setValue, KnownSymbol attr, Typeable attr, Eq setValue, Typeable setValue) => AttrLabelProxy (attr :: Symbol) -> setValue -> Attribute widget event
- GI.Gtk.Declarative.Attributes: [OnSignalImpure] :: (GObject widget, SignalInfo info, callback ~ HaskellCallbackType info, Functor (ImpureCallback callback widget), ToGtkCallback (ImpureCallback callback widget), (widget -> callback) ~ CustomGtkCallback (ImpureCallback callback widget)) => SignalProxy widget info -> ImpureCallback callback widget event -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: [OnSignalImpure] :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Impure) => SignalProxy widget info -> EventHandler gtkCallback widget Impure event -> Attribute widget event
- GI.Gtk.Declarative.Attributes: [OnSignalPure] :: (GObject widget, SignalInfo info, callback ~ HaskellCallbackType info, Functor (PureCallback callback), ToGtkCallback (PureCallback callback), callback ~ CustomGtkCallback (PureCallback callback)) => SignalProxy widget info -> PureCallback callback event -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: [OnSignalPure] :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Pure) => SignalProxy widget info -> EventHandler gtkCallback widget Pure event -> Attribute widget event
- GI.Gtk.Declarative.Attributes: on :: (GObject widget, SignalInfo info, callback ~ HaskellCallbackType info, pure ~ ToPureCallback callback event, Functor (PureCallback callback), ToGtkCallback (PureCallback callback), callback ~ CustomGtkCallback (PureCallback callback)) => SignalProxy widget info -> pure -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: on :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Pure, ToEventHandler gtkCallback widget Pure, userEventHandler ~ UserEventHandler gtkCallback widget Pure event) => SignalProxy widget info -> userEventHandler -> Attribute widget event
- GI.Gtk.Declarative.Attributes: onM :: (GObject widget, SignalInfo info, callback ~ HaskellCallbackType info, impure ~ ToImpureCallback callback event, withWidget ~ (widget -> impure), Functor (ImpureCallback callback widget), ToGtkCallback (ImpureCallback callback widget), (widget -> callback) ~ CustomGtkCallback (ImpureCallback callback widget)) => SignalProxy widget info -> withWidget -> Attribute widget event
+ GI.Gtk.Declarative.Attributes: onM :: (GObject widget, SignalInfo info, gtkCallback ~ HaskellCallbackType info, ToGtkCallback gtkCallback Impure, ToEventHandler gtkCallback widget Impure, userEventHandler ~ UserEventHandler gtkCallback widget Impure event) => SignalProxy widget info -> userEventHandler -> Attribute widget event
- GI.Gtk.Declarative.Attributes.Internal: addSignalHandler :: (IsWidget widget, MonadIO m) => (event -> IO ()) -> widget -> Attribute widget event -> m (Maybe Subscription)
+ GI.Gtk.Declarative.Attributes.Internal: addSignalHandler :: (IsWidget widget, MonadIO m) => (event -> IO ()) -> widget -> Attribute widget event -> m Subscription
- GI.Gtk.Declarative.Bin: bin :: (Patchable (Bin widget child), Typeable widget, Typeable child, Typeable event, Functor child, IsContainer widget, IsBin widget, IsWidget widget, FromWidget (Bin widget child) event target, BinChild widget child) => (ManagedPtr widget -> widget) -> [Attribute widget event] -> child event -> target
+ GI.Gtk.Declarative.Bin: bin :: (Patchable (Bin widget child), Typeable widget, Typeable child, Typeable event, Functor child, IsContainer widget, IsBin widget, IsWidget widget, FromWidget (Bin widget child) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> child event -> target
- GI.Gtk.Declarative.Container: container :: (Patchable (Container widget (Children child)), Typeable widget, Typeable child, Typeable event, Functor child, IsWidget widget, IsContainer widget, FromWidget (Container widget (Children child)) event target) => (ManagedPtr widget -> widget) -> [Attribute widget event] -> MarkupOf child event () -> target
+ GI.Gtk.Declarative.Container: container :: (Patchable (Container widget (Children child)), Typeable widget, Typeable child, Typeable event, Functor child, IsWidget widget, IsContainer widget, FromWidget (Container widget (Children child)) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> MarkupOf child event () -> target
- GI.Gtk.Declarative.Container.Patch: patchInContainer :: (IsWidget container, IsContainer container, Patchable child, IsContainer container child) => container -> [child e1] -> [child e2] -> IO ()
+ GI.Gtk.Declarative.Container.Patch: patchInContainer :: (IsWidget container, IsContainer container, Patchable child, IsContainer container child) => StateTree 'ContainerState container child event cs -> container -> Vector (child e1) -> Vector (child e2) -> IO (StateTree 'ContainerState container child event cs)
- GI.Gtk.Declarative.EventSource: subscribe :: EventSource widget => widget event -> Widget -> (event -> IO ()) -> IO Subscription
+ GI.Gtk.Declarative.EventSource: subscribe :: EventSource widget => widget event -> SomeState -> (event -> IO ()) -> IO Subscription
- GI.Gtk.Declarative.Markup: multiple :: [widget event] -> MarkupOf widget event ()
+ GI.Gtk.Declarative.Markup: multiple :: Vector (widget event) -> MarkupOf widget event ()
- GI.Gtk.Declarative.Markup: runMarkup :: MarkupOf widget event () -> [widget event]
+ GI.Gtk.Declarative.Markup: runMarkup :: MarkupOf widget event () -> Vector (widget event)
- GI.Gtk.Declarative.Patch: Modify :: (Widget -> IO ()) -> Patch
+ GI.Gtk.Declarative.Patch: Modify :: (IO SomeState) -> Patch
- GI.Gtk.Declarative.Patch: Replace :: (IO Widget) -> Patch
+ GI.Gtk.Declarative.Patch: Replace :: (IO SomeState) -> Patch
- GI.Gtk.Declarative.Patch: create :: Patchable widget => widget e -> IO Widget
+ GI.Gtk.Declarative.Patch: create :: Patchable widget => widget e -> IO SomeState
- GI.Gtk.Declarative.Patch: patch :: Patchable widget => widget e1 -> widget e2 -> Patch
+ GI.Gtk.Declarative.Patch: patch :: Patchable widget => SomeState -> widget e1 -> widget e2 -> Patch
- GI.Gtk.Declarative.SingleWidget: widget :: (Typeable widget, Typeable event, Functor (Attribute widget), IsWidget widget, FromWidget (SingleWidget widget) event target) => (ManagedPtr widget -> widget) -> [Attribute widget event] -> target
+ GI.Gtk.Declarative.SingleWidget: widget :: (Typeable widget, Typeable event, Functor (Attribute widget), IsWidget widget, FromWidget (SingleWidget widget) event target) => (ManagedPtr widget -> widget) -> Vector (Attribute widget event) -> target

Files

CHANGELOG.md view
@@ -1,3 +1,10 @@+* 0.2.0+    - Introduce shadow state (breaking change!)+    - Optimized patching (2x-7x faster!)+    - Many bug fixes in patching+    - Reimplement callback conversions+    - Return pairs in declarative event handlers, for non-`()` GTK+ callback return values+ * 0.1.0     - First version of `gi-gtk-declarative`!     - Basic widget without event handling
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
+ bench/Benchmark.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE OverloadedLabels  #-}+{-# LANGUAGE OverloadedLists   #-}+{-# LANGUAGE OverloadedStrings #-}+module Main where++import           Control.Concurrent+import           Control.Monad+import           Criterion.Main+import           Data.Text+import qualified GI.Gdk                   as Gdk+import qualified GI.GLib.Constants        as GLib++import           GI.Gtk                   (Box (..), Label (..), Window (..))+import qualified GI.Gtk                   as Gtk+import           GI.Gtk.Declarative+import           GI.Gtk.Declarative.State++testView :: [Int] -> Widget ()+testView ns = bin Window [] $ container Box [] $ forM_ ns $ \n ->+  boxChild True True 10+    $ widget Label [#label := pack (show n), classes ["a", "b"]]++testPatch state oldView newView = case patch state oldView newView of+  Modify ma -> do+    ret <- newEmptyMVar+    void . Gdk.threadsAddIdle GLib.PRIORITY_DEFAULT $ do+      ma >>= putMVar ret+      return False+    takeMVar ret+  _ -> error "Expected a modification."++main :: IO ()+main = do+  _ <- Gtk.init Nothing+  let initialView = testView [1 .. 100]+  initialState <- create initialView+  #showAll =<< someStateWidget initialState+  _ <- forkOS $ do+    defaultMain+      [ bgroup+          "patch"+          [ bench "Modify (equal)" . whnfIO . replicateM_ 10 $ do+            s1 <- testPatch initialState initialView initialView+            void $ testPatch s1 initialView initialView+          , bench "Modify (diff)" . whnfIO . replicateM_ 10 $ do+            s1 <- testPatch initialState initialView initialView+            void $ testPatch s1 initialView (testView [2 .. 101])+          ]+      ]+    Gtk.mainQuit+  Gtk.main
gi-gtk-declarative.cabal view
@@ -1,5 +1,5 @@ name:                 gi-gtk-declarative-version:              0.1.0+version:              0.2.0 synopsis:             Declarative GTK+ programming in Haskell description:          A declarative programming model for GTK+ user                       interfaces, implementing support for various widgets@@ -23,16 +23,21 @@ library   exposed-modules:      GI.Gtk.Declarative                       , GI.Gtk.Declarative.Attributes+                      , GI.Gtk.Declarative.Attributes.Collected                       , GI.Gtk.Declarative.Attributes.Internal+                      , GI.Gtk.Declarative.Attributes.Internal.EventHandler+                      , GI.Gtk.Declarative.Attributes.Internal.Conversions                       , GI.Gtk.Declarative.Bin                       , GI.Gtk.Declarative.Container                       , GI.Gtk.Declarative.Container.Box+                      , GI.Gtk.Declarative.Container.Class+                      , GI.Gtk.Declarative.Container.MenuItem                       , GI.Gtk.Declarative.Container.Patch-                      , GI.Gtk.Declarative.CSS                       , 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-gobject             >= 2    && <3                       , gi-gtk                 >= 3    && <4@@ -42,6 +47,22 @@                       , mtl                       , text                       , unordered-containers >= 0.2 && < 0.3+                      , vector   hs-source-dirs:       src   default-language:     Haskell2010   ghc-options:          -Wall++benchmark gi-gtk-declarative-benchmark+  type:              exitcode-stdio-1.0+  hs-source-dirs:    bench+  main-is:           Benchmark.hs+  build-depends:     base                 >= 4        && < 5+                   , criterion+                   , gi-glib+                   , gi-gdk+                   , gi-gtk+                   , gi-gtk-declarative+                   , random+                   , text+  ghc-options:       -Wall -O2 -rtsopts -with-rtsopts=-N -threaded+  default-language:  Haskell2010
src/GI/Gtk/Declarative.hs view
@@ -9,11 +9,12 @@   ( module Export   ) where -import           GI.Gtk.Declarative.Attributes    as Export-import           GI.Gtk.Declarative.Bin           as Export-import           GI.Gtk.Declarative.Container     as Export-import           GI.Gtk.Declarative.Container.Box as Export-import           GI.Gtk.Declarative.CSS           as Export-import           GI.Gtk.Declarative.Markup        as Export-import           GI.Gtk.Declarative.Patch         as Export-import           GI.Gtk.Declarative.SingleWidget  as Export+import           GI.Gtk.Declarative.Attributes         as Export+import           GI.Gtk.Declarative.Bin                as Export (Bin, bin)+import           GI.Gtk.Declarative.Container          as Export (Container,+                                                                  container)+import           GI.Gtk.Declarative.Container.Box      as Export+import           GI.Gtk.Declarative.Container.MenuItem as Export+import           GI.Gtk.Declarative.Markup             as Export+import           GI.Gtk.Declarative.Patch              as Export+import           GI.Gtk.Declarative.SingleWidget       as Export
src/GI/Gtk/Declarative/Attributes.hs view
@@ -1,44 +1,44 @@+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}+ {-# LANGUAGE DataKinds              #-} {-# LANGUAGE FlexibleContexts       #-} {-# LANGUAGE FlexibleInstances      #-}-{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs                  #-}-{-# LANGUAGE KindSignatures         #-} {-# LANGUAGE LambdaCase             #-} {-# LANGUAGE MultiParamTypeClasses  #-} {-# LANGUAGE OverloadedLabels       #-}-{-# LANGUAGE RankNTypes             #-}-{-# LANGUAGE ScopedTypeVariables    #-}-{-# LANGUAGE TypeApplications       #-} {-# LANGUAGE TypeFamilies           #-}-{-# LANGUAGE TypeOperators          #-}  -- | Attribute lists on declarative objects, supporting the underlying -- attributes from "Data.GI.Base.Attributes", along with CSS class lists, and--- pure and impure event callbacks.+-- pure and impure event EventHandlers.  module GI.Gtk.Declarative.Attributes   ( Attribute(..)-  -- *   , classes+  , afterCreated+  , ClassSet   -- * Event Handling   , on   , onM-  -- * Callbacks-  , ToGtkCallback(..)+  -- * EventHandlers+  , EventHandler(..)   ) where -import           Control.Monad                  (void)-import qualified Data.GI.Base.Attributes        as GI-import qualified Data.GI.Base.Signals           as GI-import qualified Data.HashSet                   as HashSet-import           Data.Text                      (Text)+import qualified Data.GI.Base.Attributes                            as GI+import qualified Data.GI.Base.Signals                               as GI+import           Data.HashSet (HashSet)+import qualified Data.HashSet                                       as HashSet+import qualified Data.Text                                          as T+import           Data.Text    (Text) import           Data.Typeable-import           GHC.TypeLits                   (KnownSymbol, Symbol)-import qualified GI.Gtk                         as Gtk+import           GHC.TypeLits                                       (KnownSymbol,+                                                                     Symbol)+import qualified GI.Gtk                                             as Gtk -import           GI.Gtk.Declarative.CSS+import           GI.Gtk.Declarative.Attributes.Internal.EventHandler+import           GI.Gtk.Declarative.Attributes.Internal.Conversions  -- * Attributes @@ -57,6 +57,8 @@       , GI.AttrSetTypeConstraint info setValue       , KnownSymbol attr       , Typeable attr+      , Eq setValue+      , Typeable setValue       )    => GI.AttrLabelProxy (attr :: Symbol) -> setValue -> Attribute widget event   -- | Defines a set of CSS classes for the underlying widget's style context.@@ -65,163 +67,83 @@     :: Gtk.IsWidget widget     => ClassSet     -> Attribute widget event-  -- | Emit events using a pure callback. Use the 'on function instead of this+  -- | Emit events using a pure event handler. Use the 'on' function, instead of this   -- constructor directly.   OnSignalPure     :: ( Gtk.GObject widget        , GI.SignalInfo info-       , callback ~ GI.HaskellCallbackType info-       , Functor (PureCallback callback)-       , ToGtkCallback (PureCallback callback)-       , callback ~ CustomGtkCallback (PureCallback callback)+       , gtkCallback ~ GI.HaskellCallbackType info+       , ToGtkCallback gtkCallback Pure        )     => Gtk.SignalProxy widget info-    -> PureCallback callback event+    -> EventHandler gtkCallback widget Pure event     -> Attribute widget event-  -- | Emit events using an impure callback. Use the 'on function instead of-  -- this constructor directly.+  -- | Emit events using a pure event handler. Use the 'on' function, instead of this+  -- constructor directly.   OnSignalImpure     :: ( Gtk.GObject widget        , GI.SignalInfo info-       , callback ~ GI.HaskellCallbackType info-       , Functor (ImpureCallback callback widget)-       , ToGtkCallback (ImpureCallback callback widget)-       , (widget -> callback) ~ CustomGtkCallback (ImpureCallback callback widget)+       , gtkCallback ~ GI.HaskellCallbackType info+       , ToGtkCallback gtkCallback Impure        )     => Gtk.SignalProxy widget info-    -> ImpureCallback callback widget event+    -> EventHandler gtkCallback widget Impure event     -> Attribute widget event+  -- | Provide a callback to modify the widget after it's been created.+  AfterCreated+    :: (widget -> IO ())+    -> Attribute widget event --- | Attributes have a 'Functor' instance that maps events in all event--- callbacks.+-- | A set of CSS classes.+type ClassSet = HashSet Text++-- | Attributes have a 'Functor' instance that maps events in all+-- event handler. instance Functor (Attribute widget) where   fmap f = \case     attr := value -> attr := value     Classes cs -> Classes cs-    OnSignalPure signal cb -> OnSignalPure signal (fmap f cb)-    OnSignalImpure signal cb -> OnSignalImpure signal (fmap f cb)+    OnSignalPure signal eh -> OnSignalPure signal (fmap f eh)+    OnSignalImpure signal eh -> OnSignalImpure signal (fmap f eh)+    AfterCreated eh -> AfterCreated eh  -- | Define the CSS classes for the underlying widget's style context. For these -- classes to have any effect, this requires a 'Gtk.CssProvider' with CSS files -- loaded, to be added to the GDK screen. You probably want to do this in your -- entry point when setting up GTK.-classes :: Gtk.IsWidget widget => [Text] -> Attribute widget event+classes :: Gtk.IsWidget widget => [T.Text] -> Attribute widget event classes = Classes . HashSet.fromList --- | Emit events, using a pure callback, by subcribing to the specified+-- | Emit events, using a pure event handler, by subcribing to the specified -- signal. on   :: ( Gtk.GObject widget      , GI.SignalInfo info-     , callback ~ GI.HaskellCallbackType info-     , pure ~ ToPureCallback callback event-     , Functor (PureCallback callback)-     , ToGtkCallback (PureCallback callback)-     , callback ~ CustomGtkCallback (PureCallback callback)+     , gtkCallback ~ GI.HaskellCallbackType info+     , ToGtkCallback gtkCallback Pure+     , ToEventHandler gtkCallback widget Pure+     , userEventHandler ~ UserEventHandler gtkCallback widget Pure event      )   => Gtk.SignalProxy widget info-  -> pure+  -> userEventHandler   -> Attribute widget event-on signal = OnSignalPure signal . PureCallback+on signal = OnSignalPure signal . toEventHandler --- | Emit events, using an impure callback receiving the 'widget' and returning+-- | Emit events, using an impure event handler receiving the 'widget' and returning -- an 'IO' action of 'event', by subcribing to the specified signal. onM   :: ( Gtk.GObject widget      , GI.SignalInfo info-     , callback ~ GI.HaskellCallbackType info-     , impure ~ ToImpureCallback callback event-     , withWidget ~ (widget -> impure)-     , Functor (ImpureCallback callback widget)-     , ToGtkCallback (ImpureCallback callback widget)-     , (widget -> callback) ~ CustomGtkCallback (ImpureCallback callback widget)+     , gtkCallback ~ GI.HaskellCallbackType info+     , ToGtkCallback gtkCallback Impure+     , ToEventHandler gtkCallback widget Impure+     , userEventHandler ~ UserEventHandler gtkCallback widget Impure event      )   => Gtk.SignalProxy widget info-  -> withWidget+  -> userEventHandler   -> Attribute widget event-onM signal = OnSignalImpure signal . ImpureCallback---- * Pure Callbacks---- | Convert a GTK+ callback type to a pure callback type, i.e. a type--- without 'IO'. The pure callback is either a single 'event', or a function of--- the same arity and arguments as the GTK+ callback, but with the 'event' as--- the range.-type family ToPureCallback gtkCallback event where-  ToPureCallback (IO ()) event = event-  ToPureCallback (a -> b) event = a -> ToPureCallback b event---- | A 'PureCallback' holds a pure callback, as defined by 'ToPureCallback'.-data PureCallback callback event where-  PureCallback-    :: (pure ~ ToPureCallback callback event)-    => pure-    -> PureCallback callback event---- The functor instances are pretty annoying, being repeated for each function--- arity. Could this be done in another way?--instance Functor (PureCallback (IO ())) where-  fmap f (PureCallback e) = PureCallback (f e)--instance Functor (PureCallback (x -> IO ())) where-  fmap f (PureCallback g) = PureCallback (f . g)--instance Functor (PureCallback (x -> y -> IO ())) where-  fmap f (PureCallback g) = PureCallback (\x -> f . g x)---- * Impure Callbacks---- | Convert a GTK+ callback type to an impure callback type, i.e. a type--- with 'IO event' as the range, instead of 'IO ()'. The impure callback is--- either a single 'IO event', or a function of the same arity and arguments as--- the GTK+ callback, but with 'IO event' as the range.-type family ToImpureCallback t e where-  ToImpureCallback (IO ()) e  = IO e-  ToImpureCallback (a -> b) e = a -> ToImpureCallback b e---- | An 'ImpureCallback' holds an impure callback, as defined by--- 'ToImpureCallback', but with an extra 'widget' argument. This is so that--- impure callbacks can query their underlying GTK+ widgets for data.-data ImpureCallback callback widget event where-  ImpureCallback-    :: (impure ~ ToImpureCallback callback event)-    => (widget -> impure)-    -> ImpureCallback callback widget event---- The functor instances are pretty annoying, being repeated for each function--- arity. Could this be done in another way?--instance Functor (ImpureCallback (IO ()) widget) where-  fmap f (ImpureCallback g) = ImpureCallback (\w -> f <$> g w)--instance Functor (ImpureCallback (x -> IO ()) widget) where-  fmap f (ImpureCallback g) = ImpureCallback (\w -> fmap f . g w)--instance Functor (ImpureCallback (x -> y -> IO ()) widget) where-  fmap f (ImpureCallback g) = ImpureCallback (\w x -> fmap f . g w x)---- * GTK+ Callback Conversions---- | Internal class for converting user callbacks to gi-gtk callbacks.-class ToGtkCallback userCallback where-  type CustomGtkCallback userCallback :: *-  -- | Converts a user callback, i.e. a pure or an impure callback, back to a-  -- GTK+ callback.-  toGtkCallback :: userCallback event -> (event -> IO ()) -> CustomGtkCallback userCallback--instance ToGtkCallback (PureCallback (IO ())) where-  type CustomGtkCallback (PureCallback (IO ())) = IO ()-  toGtkCallback (PureCallback cb) f = void (f cb)--instance ToGtkCallback (PureCallback (x -> IO ())) where-  type CustomGtkCallback (PureCallback (x -> IO ())) = x -> IO ()-  toGtkCallback (PureCallback cb) f x = void (f (cb x))--instance ToGtkCallback (PureCallback (x -> y -> IO ()))  where-  type CustomGtkCallback (PureCallback (x -> y -> IO ())) = x -> y -> IO ()-  toGtkCallback (PureCallback cb) f x y = void (f (cb x y))+onM signal = OnSignalImpure signal . toEventHandler -instance ToGtkCallback (ImpureCallback (IO ()) widget)  where-  type CustomGtkCallback (ImpureCallback (IO ()) widget) = widget -> IO ()-  toGtkCallback (ImpureCallback cb) f w = void (cb w >>= f)+-- | Provide a EventHandler to modify the widget after it's been created.+afterCreated :: (widget -> IO ()) -> Attribute widget event+afterCreated = AfterCreated
+ src/GI/Gtk/Declarative/Attributes/Collected.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE DataKinds  #-}+{-# LANGUAGE ScopedTypeVariables  #-}+{-# LANGUAGE TypeApplications  #-}+{-# LANGUAGE RecordWildCards  #-}+{-# LANGUAGE GADTs      #-}+{-# LANGUAGE LambdaCase #-}++-- | Internal helpers for applying attributes and signal handlers to GTK++-- widgets.+module GI.Gtk.Declarative.Attributes.Collected+  ( CollectedProperties+  , Collected(..)+  , collectAttributes+  , constructProperties+  , updateProperties+  , updateClasses+  )+where++import           Data.Foldable+import qualified Data.Text                     as Text+import           Data.Text                                ( Text )+import qualified Data.HashSet                  as HashSet+import qualified Data.HashMap.Strict           as HashMap+import           Data.HashMap.Strict                      ( HashMap )+import qualified Data.GI.Base.Attributes       as GI+import qualified GI.Gtk                        as Gtk+import           Data.Typeable+import           GHC.TypeLits+import           Data.Vector                              ( Vector )++import           GI.Gtk.Declarative.Attributes++-- | A collected property key/value pair, to be used when+-- settings properties when patching widgets.+data CollectedProperty widget where+  CollectedProperty+    :: (GI.AttrOpAllowed 'GI.AttrConstruct info widget+      , GI.AttrOpAllowed 'GI.AttrSet info widget+      , GI.AttrGetC info widget attr getValue+      , GI.AttrSetTypeConstraint info setValue+      , KnownSymbol attr+      , Typeable attr+      , Eq setValue+      , Typeable setValue+      )+   => GI.AttrLabelProxy attr -> setValue -> CollectedProperty widget++-- | A collected map of key/value pairs, where the type-level property+-- names are represented as 'Text' values. This is used to calculate+-- differences in old and new property sets when patching.+type CollectedProperties widget = HashMap Text (CollectedProperty widget)++-- | All the collected properties and classes for a widget. These are based+-- on the 'Attribute' list in the declarative markup, but collected separately+-- into more efficient data structures, optimized for patching.+data Collected widget event = Collected+  { collectedClasses :: ClassSet+  , collectedProperties :: CollectedProperties widget+  }++instance Semigroup (Collected widget event) where+  c1 <> c2 =+    Collected+      (collectedClasses c1 <> collectedClasses c2)+      (collectedProperties c1 <> collectedProperties c2)++instance Monoid (Collected widget event) where+  mempty = Collected mempty mempty++-- | Collect declarative markup attributes to the patching-optimized+-- 'Collected' data structure.+collectAttributes :: Vector (Attribute widget event) -> Collected widget event+collectAttributes = foldl' go mempty+ where+  go+    :: Collected widget event+    -> Attribute widget event+    -> Collected widget event+  go Collected {..} = \case+    attr := value -> Collected+      { collectedProperties = HashMap.insert (Text.pack (symbolVal attr))+                                             (CollectedProperty attr value)+                                             collectedProperties+      , ..+      }+    Classes classSet ->+      Collected {collectedClasses = collectedClasses <> classSet, ..}+    _ -> Collected {..}++-- | Create a list of GTK construct operations based on collected+-- properties, used when creating new widgets.+constructProperties+  :: Collected widget event -> [GI.AttrOp widget 'GI.AttrConstruct]+constructProperties c = map+  (\(CollectedProperty attr value) -> attr Gtk.:= value)+  (HashMap.elems (collectedProperties c))++-- | Update the changed properties of a widget, based on the old and new+-- collected properties.+updateProperties+  :: widget -> CollectedProperties widget -> CollectedProperties widget -> IO ()+updateProperties (widget' :: widget) oldProps newProps = do+  let toAdd  = HashMap.elems (HashMap.difference newProps oldProps)+      setOps = mconcat+        (HashMap.elems (HashMap.intersectionWith toMaybeSetOp oldProps newProps)+        )+  GI.set widget' (map (toSetOp (Proxy @widget)) toAdd <> setOps)+ where+  toSetOp+    :: Proxy widget+    -> CollectedProperty widget+    -> Gtk.AttrOp widget 'GI.AttrSet+  toSetOp _ (CollectedProperty attr value) = attr Gtk.:= value++  toMaybeSetOp+    :: CollectedProperty widget+    -> CollectedProperty widget+    -> [Gtk.AttrOp widget 'GI.AttrSet]+  toMaybeSetOp (CollectedProperty attr (v1 :: t1)) (CollectedProperty _ (v2 :: t2))+    = case eqT @t1 @t2 of+      Just Refl | v1 /= v2 -> pure (attr Gtk.:= v2)+      _                    -> mempty++-- | Update the style context's classes to only include the new set of+-- classes (last argument).+updateClasses :: Gtk.StyleContext -> ClassSet -> ClassSet -> IO ()+updateClasses sc old new = do+  let toAdd    = HashSet.difference new old+      toRemove = HashSet.difference old new+  mapM_ (Gtk.styleContextAddClass sc)    toAdd+  mapM_ (Gtk.styleContextRemoveClass sc) toRemove
src/GI/Gtk/Declarative/Attributes/Internal.hs view
@@ -1,59 +1,48 @@-{-# LANGUAGE DataKinds              #-}-{-# LANGUAGE LambdaCase             #-}+{-# LANGUAGE DataKinds  #-}+{-# LANGUAGE ScopedTypeVariables  #-}+{-# LANGUAGE TypeApplications  #-}+{-# LANGUAGE RecordWildCards  #-}+{-# LANGUAGE GADTs      #-}+{-# LANGUAGE LambdaCase #-}  -- | Internal helpers for applying attributes and signal handlers to GTK+ -- widgets. module GI.Gtk.Declarative.Attributes.Internal-  ( extractAttrConstructOps-  , extractAttrSetOps-  , addClass-  , removeClass+  ( applyAfterCreated   , addSignalHandler-  ) where+  )+where -import qualified Data.GI.Base.Attributes        as GI-import qualified GI.GObject           as GI-import qualified GI.Gtk                         as Gtk-import           Control.Monad.IO.Class         (MonadIO)+import           Control.Monad                            ( (>=>) )+import           Control.Monad.IO.Class                   ( MonadIO )+import qualified GI.GObject                    as GI+import qualified GI.Gtk                        as Gtk  import           GI.Gtk.Declarative.Attributes+import           GI.Gtk.Declarative.Attributes.Internal.Conversions import           GI.Gtk.Declarative.EventSource -extractAttrConstructOps-  :: Attribute widget event -> [GI.AttrOp widget 'GI.AttrConstruct]-extractAttrConstructOps = \case-  (attr := value) -> pure (attr Gtk.:= value)-  _               -> mempty--extractAttrSetOps :: Attribute widget event -> [GI.AttrOp widget 'GI.AttrSet]-extractAttrSetOps = \case-  (attr := value) -> pure (attr Gtk.:= value)-  _               -> mempty--addClass :: MonadIO m => Gtk.StyleContext -> Attribute widget event -> m ()-addClass sc = \case-  Classes cs -> mapM_ (Gtk.styleContextAddClass sc) cs-  _          -> pure ()--removeClass :: MonadIO m => Gtk.StyleContext -> Attribute widget event -> m ()-removeClass sc = \case-  Classes cs -> mapM_ (Gtk.styleContextRemoveClass sc) cs-  _          -> pure ()+applyAfterCreated :: widget -> Attribute widget event -> IO ()+applyAfterCreated widget = \case+  (AfterCreated f) -> f widget+  _                -> return ()  addSignalHandler   :: (Gtk.IsWidget widget, MonadIO m)   => (event -> IO ())   -> widget   -> Attribute widget event-  -> m (Maybe Subscription)-addSignalHandler onEvent widget' = \case-  OnSignalPure signal handler -> do-    handlerId <- Gtk.on widget' signal (toGtkCallback handler onEvent)-    w         <- Gtk.toWidget widget'-    pure (Just (fromCancellation (GI.signalHandlerDisconnect w handlerId)))-  OnSignalImpure signal handler -> do-    handlerId <- Gtk.on widget' signal (toGtkCallback handler onEvent widget')-    w         <- Gtk.toWidget widget'-    pure (Just (fromCancellation (GI.signalHandlerDisconnect w handlerId)))-  _ -> pure Nothing-+  -> m Subscription+addSignalHandler onEvent widget' = listenToSignal >=> \case+  Just eh -> setupCancellation eh+  Nothing -> pure mempty+ where+  listenToSignal = \case+    OnSignalPure signal handler ->+      Just <$> Gtk.on widget' signal (toGtkCallback handler widget' onEvent)+    OnSignalImpure signal handler ->+      Just <$> Gtk.on widget' signal (toGtkCallback handler widget' onEvent)+    _ -> pure Nothing+  setupCancellation handlerId = do+    w <- Gtk.toWidget widget'+    pure (fromCancellation (GI.signalHandlerDisconnect w handlerId))
+ src/GI/Gtk/Declarative/Attributes/Internal/Conversions.hs view
@@ -0,0 +1,52 @@+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}++{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module GI.Gtk.Declarative.Attributes.Internal.Conversions+  ( ToGtkCallback(..)+  )+where++import           Control.Monad                            ( void )+import           Data.Functor                             ( ($>) )+import           Data.Functor.Identity++import           GI.Gtk.Declarative.Attributes.Internal.EventHandler++-- * GTK+ EventHandler Conversions++-- | Internal class for converting 'EventHandler's to gi-gtk callbacks.+class ToGtkCallback gtkCallback purity where+  -- | Converts an 'EventHandler', i.e. the internal encoding of a pure or an impure+  -- callback, back to a GTK+ callback. Impure callbacks will also receive a+  -- 'widget' as the last argument.+  toGtkCallback+    :: EventHandler gtkCallback widget purity event+    -> widget+    -> (event -> IO ())+    -> gtkCallback++instance ToGtkCallback (IO ()) Pure where+  toGtkCallback (PureEventHandler (OnlyEvent e)) _ f = void (f (runIdentity e))++instance ToGtkCallback (IO Bool) Pure where+  toGtkCallback (PureEventHandler (ReturnAndEvent re)) _ f =+    let (r, e) = runIdentity re+    in f e $> r++instance ToGtkCallback (IO ()) Impure where+  toGtkCallback (ImpureEventHandler r) w f =+    let OnlyEvent me = r w+    in me >>= f++instance ToGtkCallback (IO Bool) Impure where+  toGtkCallback (ImpureEventHandler r) w f = do+    let ReturnAndEvent re = r w+    (r', e) <- re+    f e+    return r'++instance ToGtkCallback y purity => ToGtkCallback (x -> y) purity where+  toGtkCallback (EventHandlerFunction cb) f w x = toGtkCallback (cb x) f w
+ src/GI/Gtk/Declarative/Attributes/Internal/EventHandler.hs view
@@ -0,0 +1,80 @@+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}++{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE KindSignatures        #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+module GI.Gtk.Declarative.Attributes.Internal.EventHandler+  ( Purity(..)+  , EventHandlerReturn(..)+  , EventHandler(..)+  , UserEventHandler+  , ToEventHandler(..)+  )+where++import           Data.Functor.Identity++-- | A 'EventHandler' can be either pure or impure.+data Purity = Pure | Impure++-- | The two supported types of return values in user event handlers are encoded+-- by the 'EventHandlerReturn' type; either you can return only an 'event', or if+-- the underlying GTK+ callback needs to return a 'Bool', you return+-- a @(Bool, event)@ tuple.+data EventHandlerReturn m gtkReturn event where+  OnlyEvent :: m e -> EventHandlerReturn m () e+  ReturnAndEvent :: m (Bool, e) -> EventHandlerReturn m Bool e++instance Functor m => Functor (EventHandlerReturn m gtkEventHandler) where+  fmap f = \case+    OnlyEvent e -> OnlyEvent (fmap f e)+    ReturnAndEvent mr -> ReturnAndEvent (fmap (fmap f) mr)++-- | Encodes the user event handler in such a way that we can have+-- a 'Functor' instance for arity-polymorphic event handlers.+data EventHandler gtkEventHandler widget (purity :: Purity) event where+  PureEventHandler :: EventHandlerReturn Identity ret e -> EventHandler (IO ret) w Pure e+  ImpureEventHandler :: (w -> EventHandlerReturn IO ret e) -> EventHandler (IO ret) w Impure e+  EventHandlerFunction :: (a -> EventHandler b w p e) -> EventHandler (a -> b) w p e++instance Functor (EventHandler gtkEventHandler widget purity) where+  fmap f = \case+    PureEventHandler r -> PureEventHandler (fmap f r)+    ImpureEventHandler r -> ImpureEventHandler (fmap (fmap f) r)+    EventHandlerFunction eh -> EventHandlerFunction (\a -> fmap f (eh a))++-- | Convert from a GTK+ callback type to a user event handler type (the ones+-- you'd apply 'on' and 'onM' with) based on the given widget, purity, and event+-- types.+type family UserEventHandler gtkCallback widget (purity :: Purity) event where+  UserEventHandler (IO ())   widget Pure   event = event+  UserEventHandler (IO Bool) widget Pure   event = (Bool, event)+  UserEventHandler (IO ())   widget Impure event = widget -> IO event+  UserEventHandler (IO Bool) widget Impure event = widget -> IO (Bool, event)+  UserEventHandler (a -> b)  widget purity event = a -> UserEventHandler b widget purity event++-- | Internal class for converting user event handlers to encoded 'EventHandler' values.+class ToEventHandler gtkEventHandler widget purity where+  -- | Convert from a user event handler to an 'EventHandler'.+  toEventHandler+    :: UserEventHandler gtkEventHandler widget purity event+    -> EventHandler gtkEventHandler widget purity event++instance ToEventHandler (IO ()) widget Pure where+  toEventHandler = PureEventHandler . OnlyEvent . pure++instance ToEventHandler (IO Bool) widget Pure where+  toEventHandler = PureEventHandler . ReturnAndEvent . pure++instance ToEventHandler (IO ()) widget Impure where+  toEventHandler eh = ImpureEventHandler (OnlyEvent . eh)++instance ToEventHandler (IO Bool) widget Impure where+  toEventHandler eh = ImpureEventHandler (ReturnAndEvent . eh)++instance (ToEventHandler b widget purity) => ToEventHandler (a -> b) widget purity where+  toEventHandler f = EventHandlerFunction (toEventHandler . f)
src/GI/Gtk/Declarative/Bin.hs view
@@ -4,44 +4,42 @@ {-# LANGUAGE FlexibleInstances      #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs                  #-}-{-# LANGUAGE LambdaCase             #-} {-# LANGUAGE MultiParamTypeClasses  #-} {-# LANGUAGE OverloadedLabels       #-} {-# LANGUAGE ScopedTypeVariables    #-}+{-# LANGUAGE TypeApplications       #-} {-# LANGUAGE TypeFamilies           #-} {-# LANGUAGE TypeOperators          #-}  -- | A declarative representation of 'Gtk.Bin' in GTK. module GI.Gtk.Declarative.Bin-  ( Bin+  ( Bin(..)   , bin+  , BinChild   ) where -import           Control.Monad                          ((>=>))-import           Data.Maybe import           Data.Typeable-import qualified GI.GObject                             as GI-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.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   -- | Supported 'Gtk.Bin's.-class BinChild bin (child :: * -> *) | bin -> child where-  getChild :: bin -> IO Gtk.Widget+class BinChild bin (child :: * -> *) | bin -> child  instance BinChild Gtk.ScrolledWindow Widget where-  getChild scrolledWindow = do-    viewPort <- getBinChild Gtk.Viewport scrolledWindow-    getBinChild Gtk.Widget viewPort- instance BinChild Gtk.ListBoxRow Widget where-  getChild = getBinChild Gtk.Widget+instance BinChild Gtk.Window Widget where+instance BinChild Gtk.Dialog Widget where+instance BinChild Gtk.MenuItem Widget where  -- | Declarative version of a /bin/ widget, i.e. a widget with exactly one -- child.@@ -54,7 +52,7 @@        , Functor child        )     => (Gtk.ManagedPtr widget -> widget)-    -> [Attribute widget event]+    -> Vector (Attribute widget event)     -> child event     -> Bin widget child event @@ -63,8 +61,8 @@     Bin ctor (fmap f <$> attrs) (fmap f child)  -- | Construct a /bin/ widget, i.e. a widget with exactly one child.-bin ::-     ( Patchable (Bin widget child)+bin+  :: ( Patchable (Bin widget child)      , Typeable widget      , Typeable child      , Typeable event@@ -73,10 +71,9 @@      , Gtk.IsBin widget      , Gtk.IsWidget widget      , FromWidget (Bin widget child) event target-     , BinChild widget child      )   => (Gtk.ManagedPtr widget -> widget) -- ^ A bin widget constructor from the underlying gi-gtk library.-  -> [Attribute widget event]          -- ^ List of 'Attribute's.+  -> 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'. bin ctor attrs = fromWidget . Bin ctor attrs@@ -86,34 +83,46 @@ --  instance (BinChild parent child, Patchable child) => Patchable (Bin parent child) where-  create (Bin ctor props child) = do-    let attrOps = concatMap extractAttrConstructOps props-    widget' <- Gtk.new ctor attrOps+  create (Bin ctor attrs child) = do+    let collected = collectAttributes attrs+    widget' <- Gtk.new ctor (constructProperties collected)+    Gtk.widgetShow widget'      sc <- Gtk.widgetGetStyleContext widget'-    mapM_ (addClass sc) props--    Gtk.containerAdd widget' =<< create child-    Gtk.toWidget widget'--  patch (Bin _ oldAttributes oldChild) (Bin ctor newAttributes newChild) =-    Modify $ \widget' -> do+    updateClasses sc mempty (collectedClasses collected) -      binWidget <- Gtk.unsafeCastTo ctor widget'-      Gtk.set binWidget (concatMap extractAttrSetOps newAttributes)+    -- TODO:+    -- mapM_ (applyAfterCreated widget') props -      sc <- Gtk.widgetGetStyleContext binWidget-      mapM_ (removeClass sc) oldAttributes-      mapM_ (addClass sc) newAttributes+    childState <- create child+    childWidget <- someStateWidget childState+    Gtk.containerAdd widget' childWidget+    return (SomeState (StateTreeBin (StateTreeNode widget' sc collected ()) childState)) -      childWidget <- getChild binWidget+  patch (SomeState (st :: StateTree stateType w1 c1 e1 cs))+        (Bin _ _ oldChild)+        (Bin (ctor :: Gtk.ManagedPtr w2 -> w2) newAttributes newChild) =+    case (st, eqT @w1 @w2) of+      (StateTreeBin top oldChildState, Just Refl) ->+        Modify $ do+          binWidget <- Gtk.unsafeCastTo ctor (stateTreeWidget top)+          let oldCollected = stateTreeCollectedAttributes top+              newCollected = collectAttributes newAttributes+          updateProperties binWidget (collectedProperties oldCollected) (collectedProperties newCollected)+          updateClasses (stateTreeStyleContext top) (collectedClasses oldCollected) (collectedClasses newCollected) -      case patch oldChild newChild of-        Modify modify -> modify childWidget-        Replace createNew -> do-          Gtk.containerRemove binWidget childWidget-          Gtk.containerAdd binWidget =<< createNew-        Keep -> return ()+          let top' = top { stateTreeCollectedAttributes = newCollected }+          case patch oldChildState oldChild newChild of+            Modify modify -> SomeState . StateTreeBin top' <$> modify+            Replace createNew -> do+              Gtk.widgetDestroy =<< someStateWidget oldChildState+              newChildState <- createNew+              childWidget <- someStateWidget newChildState+              Gtk.widgetShow childWidget+              Gtk.containerAdd binWidget childWidget+              return (SomeState (StateTreeBin top' newChildState))+            Keep -> return (SomeState st)+      _ -> Replace (create (Bin ctor newAttributes newChild))  -- -- EventSource@@ -121,12 +130,13 @@  instance (BinChild parent child, EventSource child) =>          EventSource (Bin parent child) where-  subscribe (Bin ctor props child) widget' cb = do-    binWidget <- Gtk.unsafeCastTo ctor widget'-    handlers' <--      mconcat . catMaybes <$> mapM (addSignalHandler cb binWidget) props-    childWidget <- getChild binWidget-    (<> handlers') <$> subscribe child childWidget cb+  subscribe (Bin ctor props child) (SomeState st) cb =+    case st of+      StateTreeBin top childState -> do+        binWidget <- Gtk.unsafeCastTo ctor (stateTreeWidget top)+        handlers' <- foldMap (addSignalHandler cb binWidget) props+        (<> handlers') <$> subscribe child childState cb+      _ -> error "Cannot subscribe to Bin events with a non-bin state tree."  -- -- FromWidget@@ -154,14 +164,5 @@          FromWidget (Bin widget child) event (Markup event a) where   fromWidget = single . Widget ---- | Get a "Gtk.Bin" child, or fail, and cast it to the given widget type.-getBinChild-  :: (Gtk.IsBin bin, GI.GObject child)-  => (Gtk.ManagedPtr child -> child)-  -> bin-  -> IO child-getBinChild ctor =-  Gtk.binGetChild-    >=> maybe (fail "expected Bin to have a child") return-    >=> Gtk.unsafeCastTo ctor+instance FromWidget (Bin widget child) event (Bin widget child event) where+  fromWidget = id
− src/GI/Gtk/Declarative/CSS.hs
@@ -1,35 +0,0 @@--- | Helpers for modifying CSS classes in 'Gtk.StyleContext'--- objects.--module GI.Gtk.Declarative.CSS where--import           Data.HashSet (HashSet)-import qualified Data.HashSet as HashSet-import           Data.Text    (Text)-import qualified GI.Gtk       as Gtk---- | A set of CSS classes.-type ClassSet = HashSet Text---- | Add the classes to the widget's style context.-addClasses-  :: Gtk.IsWidget w-  => w        -- ^ The widget to add classes to.-  -> ClassSet -- ^ New classes to add.-  -> IO ()-addClasses widget cs = do-  sc <- Gtk.widgetGetStyleContext widget-  mapM_ (Gtk.styleContextAddClass sc) cs---- | Replace all classes in 'old' with the ones in 'old' in the widget's style--- context.-replaceClasses-  :: Gtk.IsWidget w-  => w        -- ^ The widget to replace classes in.-  -> ClassSet -- ^ Old classes to replace.-  -> ClassSet -- ^ New classes to replace with.-  -> IO ()-replaceClasses widget old new = do-  sc <- Gtk.widgetGetStyleContext widget-  mapM_ (Gtk.styleContextRemoveClass sc) (HashSet.difference old new)-  mapM_ (Gtk.styleContextAddClass sc)    (HashSet.difference new old)
src/GI/Gtk/Declarative/Container.hs view
@@ -1,36 +1,39 @@ {-# 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 ScopedTypeVariables    #-}-{-# LANGUAGE TypeFamilies           #-}-{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DeriveFunctor         #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels      #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeApplications      #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}  -- | Implementations for common "Gtk.Container".  module GI.Gtk.Declarative.Container   ( Container   , container+  , Children   ) where -import           Control.Monad                      (forM_)-import           Data.Maybe+import           Control.Monad                           (forM) import           Data.Typeable-import qualified GI.Gtk                             as Gtk+import           Data.Vector                             (Vector)+import qualified Data.Vector                             as Vector+import qualified GI.Gtk                                  as Gtk  import           GI.Gtk.Declarative.Attributes+import           GI.Gtk.Declarative.Attributes.Collected import           GI.Gtk.Declarative.Attributes.Internal 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  -- | Declarative version of a /container/ widget, i.e. a widget with zero -- or more child widgets. The type of 'children' is parameterized, and differs@@ -45,7 +48,7 @@        , Functor children        )     => (Gtk.ManagedPtr widget -> widget)-    -> [Attribute widget event]+    -> Vector (Attribute widget event)     -> children event     -> Container widget children event @@ -65,12 +68,12 @@      , FromWidget (Container widget (Children child)) event target      )   => (Gtk.ManagedPtr widget -> widget) -- ^ A container widget constructor from the underlying gi-gtk library.-  -> [Attribute widget event]          -- ^ List of 'Attribute's.+  -> 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 :: [child event] }+newtype Children child event = Children { unChildren :: Vector (child event) }   deriving (Functor)  toChildren :: MarkupOf child event () -> Children child event@@ -80,43 +83,57 @@ -- Patchable -- -instance (Patchable child, IsContainer container child) =>+instance (Patchable child, Typeable child, IsContainer container child) =>          Patchable (Container container (Children child)) where-  create (Container ctor props children) = do-    let attrOps = concatMap extractAttrConstructOps props-    widget' <- Gtk.new ctor attrOps+  create (Container ctor attrs children) = do+    let collected = collectAttributes attrs+    widget' <- Gtk.new ctor (constructProperties collected)+    Gtk.widgetShow widget'     sc <- Gtk.widgetGetStyleContext widget'-    mapM_ (addClass sc) props-    forM_ (unChildren children) $ \child -> do-      childWidget <- create child-      appendChild widget' child childWidget-    Gtk.toWidget widget'-  patch (Container _ oldAttributes oldChildren) (Container ctor newAttributes newChildren) =-    Modify $ \widget' -> do-      containerWidget <- Gtk.unsafeCastTo ctor widget'-      Gtk.set containerWidget (concatMap extractAttrSetOps newAttributes)-      sc <- Gtk.widgetGetStyleContext widget'-      mapM_ (removeClass sc) oldAttributes-      mapM_ (addClass sc) newAttributes-      patchInContainer-        containerWidget-        (unChildren oldChildren)-        (unChildren newChildren)+    updateClasses sc mempty (collectedClasses collected)+    -- TODO:+    -- mapM_ (applyAfterCreated widget') props+    childStates <-+      forM (unChildren children) $ \child -> do+        childState <- create child+        appendChild widget' child =<< someStateWidget childState+        return childState+    return (SomeState (StateTreeContainer (StateTreeNode widget' sc collected ()) childStates))+  patch (SomeState (st :: StateTree stateType w1 c1 e1 cs)) (Container _ _ oldChildren) new@(Container (ctor :: Gtk.ManagedPtr w2 -> w2) newAttributes (newChildren :: Children c2 e2)) =+    case (st, eqT @w1 @w2) of+      (StateTreeContainer top childStates, Just Refl) ->+        Modify $ do+          containerWidget <- Gtk.unsafeCastTo ctor (stateTreeWidget top)+          let oldCollected = stateTreeCollectedAttributes top+              newCollected = collectAttributes newAttributes+          updateProperties containerWidget (collectedProperties oldCollected) (collectedProperties newCollected)+          updateClasses (stateTreeStyleContext top) (collectedClasses oldCollected) (collectedClasses newCollected) +          let top' = top { stateTreeCollectedAttributes = newCollected }+          SomeState <$>+            patchInContainer+              (StateTreeContainer top' childStates)+              containerWidget+              (unChildren oldChildren)+              (unChildren newChildren)+      _ -> Replace (create new)+ -- -- EventSource --  instance (Typeable child, EventSource child) =>          EventSource (Container widget (Children child)) where-  subscribe (Container ctor props children) widget' cb = do-    parentWidget <- Gtk.unsafeCastTo ctor widget'-    handlers' <- mconcat . catMaybes <$> mapM (addSignalHandler cb parentWidget) props-    childWidgets <- Gtk.containerGetChildren parentWidget-    subs <--      flip foldMap (zip (unChildren children) childWidgets) $ \(c, w) ->-        subscribe c w cb-    return (handlers' <> subs)+  subscribe (Container ctor props children) (SomeState st) cb =+    case st of+      StateTreeContainer top childStates -> do+        parentWidget <- Gtk.unsafeCastTo ctor (stateTreeWidget top)+        handlers' <- foldMap (addSignalHandler cb parentWidget) props+        subs <-+          flip foldMap (Vector.zip (unChildren children) childStates) $ \(c, childState) ->+            subscribe c childState cb+        return (handlers' <> subs)+      _ -> error "Warning: Cannot subscribe to Container events with a non-container state tree."  -- -- FromWidget@@ -144,3 +161,5 @@          ) =>          FromWidget (Container widget children) event (Markup event a) where   fromWidget = single . Widget+instance FromWidget (Container widget children) event (Container widget children event) where+  fromWidget = id
src/GI/Gtk/Declarative/Container/Box.hs view
@@ -46,7 +46,7 @@  instance Patchable BoxChild where   create = create . child-  patch b1 b2 = patch (child b1) (child b2)+  patch s b1 b2 = patch s (child b1) (child b2)  instance EventSource BoxChild where   subscribe BoxChild{..} = subscribe child
+ src/GI/Gtk/Declarative/Container/Class.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE FunctionalDependencies #-}+module GI.Gtk.Declarative.Container.Class where++import qualified GI.Gtk                           as Gtk+import           Data.Int                         (Int32)++-- | Describes supported GTK+ containers and their specialized APIs for+-- appending and replacing child widgets.+class IsContainer container child | container -> child where+  -- | Append a child widget to the container.+  appendChild+    :: container    -- ^ Container widget+    -> child event  -- ^ Declarative child widget+    -> Gtk.Widget   -- ^ GTK child widget to append+    -> IO ()+  -- | Replace the child widget at the given index in the container.+  replaceChild+    :: container    -- ^ Container widget+    -> child event  -- ^ Declarative child widget+    -> Int32        -- ^ Index to replace at+    -> Gtk.Widget   -- ^ Old GTK widget to replace+    -> Gtk.Widget   -- ^ New GTK widget to replace with+    -> IO ()
+ src/GI/Gtk/Declarative/Container/MenuItem.hs view
@@ -0,0 +1,137 @@+{-# 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          #-}++module GI.Gtk.Declarative.Container.MenuItem+  ( MenuItem+  , menuItem+  , subMenu+  )+where++import           Data.Text                                ( Text )+import           Data.Typeable+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++data MenuItem event where+  MenuItem+    :: (Gtk.IsMenuItem item, BinChild item Widget, Typeable item)+    => Bin item Widget event+    -> MenuItem event+  SubMenu+    :: 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')++menuItem+  :: ( Gtk.IsMenuItem item+     , Typeable event+     , BinChild item Widget+     , Typeable item+     , Gtk.IsContainer item+     , Gtk.IsBin item+     , Gtk.IsWidget item+     )+  => (Gtk.ManagedPtr item -> item)+  -> Vector (Attribute item event)+  -> Widget event+  -> MarkupOf MenuItem event ()+menuItem item attrs = single . MenuItem . Bin item attrs++subMenu+  :: (Typeable event)+  => Text+  -> MarkupOf MenuItem event ()+  -> MarkupOf MenuItem event ()+subMenu label = single . SubMenu label . container Gtk.Menu mempty++newSubMenuItem :: Text -> IO SomeState -> IO SomeState+newSubMenuItem label createSubMenu = do+  menuItem' <- Gtk.menuItemNewWithLabel label+  sc        <- Gtk.widgetGetStyleContext menuItem'+  SomeState (subMenuState :: StateTree st subMenu children e1 cs) <- createSubMenu+  case eqT @subMenu @Gtk.Menu of+    Just Refl -> do+      Gtk.menuItemSetSubmenu menuItem' (Just (stateTreeNodeWidget subMenuState))+      return+        (SomeState+          (StateTreeBin (StateTreeNode menuItem' sc mempty ())+                        (SomeState subMenuState)+          )+        )+    Nothing -> fail "Failed to create new sub menu item."++instance Patchable MenuItem where+  create =+    \case+      MenuItem item -> create item+      SubMenu label subMenu' -> newSubMenuItem label (create subMenu')+  patch state (MenuItem (c1 :: Bin i1 Widget e1)) (MenuItem (c2 :: Bin i2 Widget e2)) =+    case eqT @i1 @i2 of+      Just Refl -> patch state c1 c2+      Nothing   -> Replace (create c2)+  patch (SomeState st) (SubMenu l1 c1) (SubMenu l2 c2) =+    case st of+      StateTreeBin top childState | l1 == l2 ->+        case patch childState c1 c2 of+          Modify modify ->+            Modify (SomeState . StateTreeBin top <$> modify)+          Replace newSubMenu ->+            Replace (newSubMenuItem l2 newSubMenu)+          Keep -> Keep+      -- TODO: case for l1 /= l2+      _ -> Replace (create (SubMenu l2 c2))+  patch _ _ b2 = Replace (create b2)++instance EventSource MenuItem where+  subscribe (MenuItem item) state cb = subscribe item state cb+  subscribe (SubMenu _ children) (SomeState st) cb =+    case st of+      StateTreeBin _ childState -> subscribe children childState cb+      _ -> error "Warning: Cannot subscribe to SubMenu events with a non-bin state tree."++instance IsContainer Gtk.MenuShell MenuItem where+  appendChild shell _ widget' =+    Gtk.menuShellAppend shell =<< Gtk.unsafeCastTo Gtk.MenuItem widget'+  replaceChild shell _ i old new = do+    Gtk.containerRemove shell old+    menuItem' <- Gtk.unsafeCastTo Gtk.MenuItem new+    Gtk.menuShellInsert shell menuItem' i+    Gtk.widgetShowAll shell++instance IsContainer Gtk.MenuBar MenuItem where+  appendChild menuBar d w = do+    s <- Gtk.toMenuShell menuBar+    appendChild s d w+  replaceChild menuBar d i old new = do+    s <- Gtk.toMenuShell menuBar+    replaceChild s d i old new++instance IsContainer Gtk.Menu MenuItem where+  appendChild menuBar d w = do+    s <- Gtk.toMenuShell menuBar+    appendChild s d w+  replaceChild menuBar d i old new = do+    s <- Gtk.toMenuShell menuBar+    replaceChild s d i old new
src/GI/Gtk/Declarative/Container/Patch.hs view
@@ -1,49 +1,36 @@-{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}+{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors -fno-warn-orphans #-} -{-# LANGUAGE DataKinds              #-}-{-# LANGUAGE FlexibleContexts       #-}-{-# LANGUAGE FlexibleInstances      #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GADTs                  #-}-{-# LANGUAGE LambdaCase             #-}-{-# LANGUAGE MultiParamTypeClasses  #-}-{-# LANGUAGE OverloadedLabels       #-}-{-# LANGUAGE RecordWildCards        #-}-{-# LANGUAGE ScopedTypeVariables    #-}-{-# LANGUAGE TypeFamilies           #-}-{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLabels      #-}+{-# LANGUAGE OverloadedLists       #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE TypeOperators         #-}  -- | This module implements the patch algorithm for containers.-module GI.Gtk.Declarative.Container.Patch (IsContainer(..), patchInContainer) where+module GI.Gtk.Declarative.Container.Patch+  ( IsContainer(..)+  , patchInContainer+  )+where -import           Control.Monad                    (forM_)-import           Data.Int                         (Int32)-import           Data.List                        (zip4)-import qualified GI.Gtk                           as Gtk+import           Data.Foldable                      (foldMap)+import           Data.Vector                        (Vector, (!?))+import qualified Data.Vector                        as Vector+import qualified GI.Gtk                             as Gtk  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----- | Describes supported GTK+ containers and their specialized APIs for--- appending and replacing child widgets.-class IsContainer container child | container -> child where-  -- | Append a child widget to the container.-  appendChild-    :: container    -- ^ Container widget-    -> child event  -- ^ Declarative child widget-    -> Gtk.Widget   -- ^ GTK child widget to append-    -> IO ()-  -- | Replace the child widget at the given index in the container.-  replaceChild-    :: container    -- ^ Container widget-    -> child event  -- ^ Declarative child widget-    -> Int32        -- ^ Index to replace at-    -> Gtk.Widget   -- ^ Old GTK widget to replace-    -> Gtk.Widget   -- ^ New GTK widget to replace with-    -> IO ()+import           GI.Gtk.Declarative.State  -- | 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@@ -54,67 +41,91 @@      , Patchable child      , IsContainer container child      )-  => container-  -> [child e1]-  -> [child e2]-  -> IO ()-patchInContainer container os' ns' = do-  cs <- Gtk.containerGetChildren container-  let maxLength = maximum [length cs, length os', length ns']-      indices   = [0 .. pred (fromIntegral maxLength)]-  forM_ (zip4 indices (padMaybes cs) (padMaybes os') (padMaybes ns')) $ \case+  => StateTree 'ContainerState container child event cs+  -> container+  -> Vector (child e1)+  -> Vector (child e2)+  -> IO (StateTree 'ContainerState container child event cs)+patchInContainer (StateTreeContainer top children) container os' ns' = do+  let maxLength = maximum ([length children, length os', length ns'] :: [Int])+      indices   = Vector.enumFromN 0 (fromIntegral maxLength)+  newChildren <- foldMap+    go+    (Vector.zip4 indices+                 (padMaybes maxLength children)+                 (padMaybes maxLength os')+                 (padMaybes maxLength ns')+    ) -    -- In case we have a corresponding old and new declarative widget, we patch-    -- the GTK widget.-    (i, Just w, Just old, Just new) -> case patch old new of-      Modify  modify       -> modify w-      Replace createWidget -> replaceChild container new i w =<< createWidget-      Keep                 -> return ()+  Gtk.widgetQueueResize container+  return (StateTreeContainer top newChildren)+  where+    go = \case+      -- In case we have a corresponding old and new declarative widget, we patch+      -- the GTK widget.+      (i, Just oldChildState, Just old, Just new) ->+        case patch oldChildState old new of+          Modify  modify       -> pure <$> modify+          Replace createWidget -> do+            newChildState  <- createWidget+            oldChildWidget <- someStateWidget oldChildState+            newChildWidget <- someStateWidget newChildState+            replaceChild container new i oldChildWidget newChildWidget+            return (pure newChildState)+          Keep -> return (pure oldChildState) -    -- When there is a new declarative widget, but there already exists a GTK-    -- widget in the corresponding place, we need to replace the GTK widget with-    -- one created from the declarative widget.-    (i, Just w, Nothing, Just new) ->-      replaceChild container new i w =<< create new+      -- When there is a new declarative widget, but there already exists a GTK+      -- widget in the corresponding place, we need to replace the GTK widget with+      -- one created from the declarative widget.+      (i, Just oldChildState, Nothing, Just new) -> do+        newChildState  <- create new+        oldChildWidget <- someStateWidget oldChildState+        newChildWidget <- someStateWidget newChildState+        replaceChild container new i oldChildWidget newChildWidget+        return (Vector.singleton newChildState) -    -- When there is a new declarative widget, or one that lacks a corresponding-    -- GTK widget, create and add it.-    (_i, Nothing, _      , Just n ) -> create n >>= appendChild container n+      -- When there is a new declarative widget, or one that lacks a corresponding+      -- GTK widget, create and add it.+      (_i, Nothing, _, Just n) -> do+        newChildState <- create n+        w             <- someStateWidget newChildState+        appendChild container n w+        return (Vector.singleton newChildState) -    -- When an declarative widget has been removed, remove the GTK widget from-    -- the container.-    (_i, Just w , Just _ , Nothing) -> Gtk.containerRemove container w+      -- When a declarative widget has been removed, remove the GTK widget from+      -- the container.+      (_i, Just childState, Just _, Nothing) -> do+        Gtk.widgetDestroy =<< someStateWidget childState+        return Vector.empty -    -- When there are more old declarative widgets than GTK widgets, we can-    -- safely ignore the old declarative widgets.-    (_i, Nothing, Just _ , Nothing) -> return ()+      -- When there are more old declarative widgets than GTK widgets, we can+      -- safely ignore the old declarative widgets.+      (_i, Nothing        , Just _ , Nothing) -> return Vector.empty -    -- But, when there are stray GTK widgets without corresponding-    -- declarative widgets, something has gone wrong, and we clean that up by-    -- removing the GTK widgets.-    (_i, Just w , Nothing, Nothing) -> Gtk.containerRemove container w+      -- But, when there are stray GTK widgets without corresponding+      -- declarative widgets, something has gone wrong, and we clean that up by+      -- removing the GTK widgets.+      (_i, Just childState, Nothing, Nothing) -> do+        Gtk.widgetDestroy =<< someStateWidget childState+        return Vector.empty -    -- No more GTK widgets or declarative widgets, we are done.-    (_i, Nothing, Nothing, Nothing) -> return ()+      -- No more GTK widgets or declarative widgets, we are done.+      (_i, Nothing, Nothing, Nothing) -> return Vector.empty -  Gtk.widgetQueueResize container+padMaybes :: Int -> Vector a -> Vector (Maybe a)+padMaybes len xs = Vector.generate len (xs !?)  instance IsContainer Gtk.ListBox (Bin Gtk.ListBoxRow Widget) where-  appendChild box _ widget' = Gtk.listBoxInsert box widget' (-1)+  appendChild box _ widget' =+    Gtk.listBoxInsert box widget' (-1)   replaceChild box _ i old new = do-    Gtk.containerRemove box old+    Gtk.widgetDestroy old     Gtk.listBoxInsert box new i-    Gtk.widgetShowAll box  instance IsContainer Gtk.Box BoxChild where   appendChild box BoxChild {..} widget' =     Gtk.boxPackStart box widget' expand fill padding-   replaceChild box boxChild' i old new = do-    Gtk.containerRemove box old+    Gtk.widgetDestroy old     appendChild box boxChild' new     Gtk.boxReorderChild box new i-    Gtk.widgetShowAll box--padMaybes :: [a] -> [Maybe a]-padMaybes xs = map Just xs ++ repeat Nothing
src/GI/Gtk/Declarative/EventSource.hs view
@@ -1,18 +1,19 @@-{-# LANGUAGE FunctionalDependencies     #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-}  -- | Event handling for declarative widgets. module GI.Gtk.Declarative.EventSource-  ( EventSource (..)+  ( EventSource(..)   , Subscription   , fromCancellation   , cancel   ) where -import qualified GI.Gtk               as Gtk+import           Data.Vector              (Vector) +import           GI.Gtk.Declarative.State+ -- | Cancel a 'Subscription', meaning that the callback will not be invoked on -- any subsequent signal emissions. cancel :: Subscription -> IO ()@@ -23,7 +24,7 @@ class EventSource widget where   subscribe     :: widget event     -- ^ Declarative widget with event handlers.-    -> Gtk.Widget       -- ^ Actual 'Gtk.Widget' that has been created or patched.+    -> SomeState        -- ^ State of rendered widget tree.     -> (event -> IO ()) -- ^ Event callback, invoked on each emitted event until                         -- the 'Subscription' is cancelled, or widget is otherwise                         -- destroyed.@@ -33,7 +34,7 @@ -- handlers (to a tree of widgets.) When subscribing to a container widget, all -- child widgets are also subscribed to, and the 'Subscription's are combined -- using the 'Semigroup' instance.-newtype Subscription = Subscription { cancellations :: [IO ()] }+newtype Subscription = Subscription { cancellations :: Vector (IO ()) }   deriving (Semigroup, Monoid)  -- | Create a subscription from a cancellation IO action.
src/GI/Gtk/Declarative/Markup.hs view
@@ -29,6 +29,7 @@  import           Control.Monad.Writer import           Data.Typeable+import           Data.Vector                    (Vector)  import           GI.Gtk.Declarative.EventSource import           GI.Gtk.Declarative.Patch@@ -54,9 +55,9 @@ -- widget instances. instance Patchable Widget where   create (Widget w) = create w-  patch (Widget (w1 :: t1 e1)) (Widget (w2 :: t2 e2)) =+  patch s (Widget (w1 :: t1 e1)) (Widget (w2 :: t2 e2)) =     case eqT @t1 @t2 of-      Just Refl -> patch w1 w2+      Just Refl -> patch s w1 w2       _         -> Replace (create w2)  instance EventSource Widget where@@ -74,11 +75,11 @@ -- 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 [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 () -> [widget event]+runMarkup :: MarkupOf widget event () -> Vector (widget event) runMarkup (MarkupOf w) = execWriter w  -- | Handy type alias for the common case of markup containing 'Widget's.@@ -86,10 +87,10 @@  -- | Construct markup from a single widget. single :: widget event -> MarkupOf widget event ()-single w = MarkupOf (tell [w])+single = MarkupOf . tell . pure  -- | Construct markup from multiple widgets.-multiple :: [widget event] -> MarkupOf widget event ()+multiple :: Vector (widget event) -> MarkupOf widget event () multiple = MarkupOf . tell  -- | Convert a widget to a target type. This is deliberately unconstrained in
src/GI/Gtk/Declarative/Patch.hs view
@@ -1,21 +1,22 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes            #-} +-- | The central patching type class of this library. module GI.Gtk.Declarative.Patch   ( Patch(..)   , Patchable(..)   ) where -import qualified GI.Gtk as Gtk+import           GI.Gtk.Declarative.State  -- | A possible action to take on an existing 'Gtk.Widget', decided by the -- 'patch' method when comparing declarative widgets. data Patch-  = Modify (Gtk.Widget -> IO ())+  = Modify (IO SomeState)   -- ^ An 'IO' action to apply to a 'Gtk.Widget' to make it reflect an updated   -- declarative widget. The action to apply is calculated from the difference   -- between the old and the new declarative widget.-  | Replace (IO Gtk.Widget)+  | Replace (IO SomeState)   -- ^ Replace the current 'Gtk.Widget' by the widget returned by the IO   -- action.   | Keep@@ -28,7 +29,7 @@   -- | Given a declarative widget that is 'Patchable', return an IO action that   -- can create a new corresponding 'Gtk.Widget'. The created widget should be   -- use in corresponding patch modifications, until it is replaced.-  create :: widget e -> IO Gtk.Widget+  create :: widget e -> IO SomeState   -- | Given two declarative widgets of the same widget type (but not   -- necessarily of the same event types,) calculate a 'Patch'.-  patch :: widget e1 -> widget e2 -> Patch+  patch :: SomeState -> widget e1 -> widget e2 -> Patch
src/GI/Gtk/Declarative/SingleWidget.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE TypeFamilies          #-} {-# LANGUAGE DataKinds             #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-}@@ -16,22 +17,24 @@   ) where -import           Data.Maybe import           Data.Typeable-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.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  -- | Declarative version of a /leaf/ widget, i.e. a widget without any children. data SingleWidget widget event where   SingleWidget     :: (Typeable widget, Gtk.IsWidget widget, Functor (Attribute widget))     => (Gtk.ManagedPtr widget -> widget)-    -> [Attribute widget event]+    -> Vector (Attribute widget event)     -> SingleWidget widget event  instance Functor (SingleWidget widget) where@@ -39,35 +42,36 @@  instance Patchable (SingleWidget widget) where   create = \case-    (SingleWidget ctor props) -> do-        let attrOps = concatMap extractAttrConstructOps props-        widget' <- Gtk.new ctor attrOps-+    SingleWidget ctor attrs -> do+        let collected = collectAttributes attrs+        widget' <- Gtk.new ctor (constructProperties collected)+        Gtk.widgetShow widget'         sc <- Gtk.widgetGetStyleContext widget'-        mapM_ (addClass sc) props+        updateClasses sc mempty (collectedClasses collected)+        -- TODO:+        -- mapM_ (applyAfterCreated widget') props -        Gtk.widgetShowAll widget'-        Gtk.toWidget widget'-  patch (SingleWidget (_    :: Gtk.ManagedPtr w1 -> w1) oldAttributes)+        return (SomeState (StateTreeWidget (StateTreeNode widget' sc collected ())))+  patch (SomeState (st :: StateTree stateType w child event cs))+        (SingleWidget (_    :: Gtk.ManagedPtr w1 -> w1) _)         (SingleWidget (ctor :: Gtk.ManagedPtr w2 -> w2) newAttributes) =-    case eqT @w1 @w2 of-      Just Refl ->-        Modify $ \widget' -> do-          w <- Gtk.unsafeCastTo ctor widget'-          Gtk.set w (concatMap extractAttrSetOps newAttributes)--          sc <- Gtk.widgetGetStyleContext widget'-          mapM_ (removeClass sc) oldAttributes-          mapM_ (addClass sc) newAttributes--          Gtk.widgetShowAll w--      Nothing -> Replace (create (SingleWidget ctor newAttributes))+    case (st, eqT @w @w1, eqT @w1 @w2) of+      (StateTreeWidget top, Just Refl, Just Refl) -> Modify $ do+        let w = stateTreeWidget top+        let oldCollected = stateTreeCollectedAttributes top+            newCollected = collectAttributes newAttributes+        updateProperties w (collectedProperties oldCollected) (collectedProperties newCollected)+        updateClasses (stateTreeStyleContext top) (collectedClasses oldCollected) (collectedClasses newCollected)+        let top' = top { stateTreeCollectedAttributes = newCollected }+        return (SomeState (StateTreeWidget top' { stateTreeCollectedAttributes = newCollected }))+      _ -> Replace (create (SingleWidget ctor newAttributes))  instance EventSource (SingleWidget widget) where-  subscribe (SingleWidget ctor props) widget' cb = do-    w <- Gtk.unsafeCastTo ctor widget'-    mconcat . catMaybes <$> mapM (addSignalHandler cb w) props+  subscribe (SingleWidget (_ :: Gtk.ManagedPtr w1 -> w1) props) (SomeState (st :: StateTree stateType w2 child event cs)) cb = do+    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@@ -81,14 +85,14 @@   fromWidget = single . Widget  -- | Construct a /leaf/ widget, i.e. one without any children.-widget ::-     ( Typeable widget+widget+  :: ( Typeable widget      , Typeable event      , Functor (Attribute widget)      , Gtk.IsWidget widget      , FromWidget (SingleWidget widget) event target      )   => (Gtk.ManagedPtr widget -> widget) -- ^ A widget constructor from the underlying gi-gtk library.-  -> [Attribute widget event]          -- ^ List of 'Attribute's.+  -> Vector (Attribute widget event)   -- ^ List of 'Attribute's.   -> target                            -- ^ The target, whose type is decided by 'FromWidget'. widget ctor = fromWidget . SingleWidget ctor
+ src/GI/Gtk/Declarative/State.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE DataKinds      #-}+{-# LANGUAGE GADTs          #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RankNTypes     #-}+-- | The 'StateTree' and 'SomeState' form a "shadow state"+-- representation, used in patching. Declarative widgets can return,+-- and later on reuse, its underlying GTK+ widget, collected+-- properties and classes, style context, custom internal state, and+-- child states. This reduces the need for querying GTK+ widgets+-- excessively, and recalculating/resetting, greatly improving the+-- performance of patching.+module GI.Gtk.Declarative.State where++import           Data.Typeable++import           Data.Vector                             (Vector)+import qualified GI.Gtk                                  as Gtk++import           GI.Gtk.Declarative.Attributes.Collected+import           GI.Gtk.Declarative.Container.Class++-- | A 'Data.Dynamic.Dynamic'-like container of a 'StateTree' value.+data SomeState where+  SomeState+    :: ( Gtk.IsWidget widget+      , Typeable widget+      , Typeable customState+      )+    => StateTree stateType widget child event customState+    -> SomeState++-- | The types of state trees that are available, matching the types+-- of GTK+ widgets (single widget, bin, and container.)+data StateType = WidgetState | BinState | ContainerState++-- | A state tree for a specific 'widget'. This is built up recursively+-- to contain child state trees, for bin and container child widgets.+data StateTree (stateType :: StateType) widget child event customState where+  StateTreeWidget+    :: !(StateTreeNode widget event customState)+    -> StateTree 'WidgetState widget child event customState+  StateTreeBin+    :: !(StateTreeNode widget event customState)+    -> SomeState+    -> StateTree 'BinState widget child event customState+  StateTreeContainer+    :: ( Gtk.IsContainer widget+       , IsContainer widget child+       )+    => !(StateTreeNode widget event customState)+    -> Vector SomeState+    -> StateTree 'ContainerState widget child event customState++-- | The common structure for all state tree nodes.+data StateTreeNode widget event customState = StateTreeNode+  { stateTreeWidget              :: !widget+  , stateTreeStyleContext        :: !Gtk.StyleContext+  , stateTreeCollectedAttributes :: !(Collected widget event)+  , stateTreeCustomState         :: customState+  }++-- * Convenience accessor functions++-- | Get the common state tree node information.+stateTreeNode+  :: StateTree stateType widget child event customState+  -> StateTreeNode widget event customState+stateTreeNode (StateTreeWidget s     ) = s+stateTreeNode (StateTreeBin       s _) = s+stateTreeNode (StateTreeContainer s _) = s++-- | Get the specific type of GTK+ widget of a state tree.+stateTreeNodeWidget :: StateTree stateType widget child event customState -> widget+stateTreeNodeWidget = stateTreeWidget . stateTreeNode++-- | Get the GTK+ widget, cast to 'Gtk.Widget', of /some/ state tree.+someStateWidget :: SomeState -> IO Gtk.Widget+someStateWidget (SomeState st) = Gtk.toWidget (stateTreeNodeWidget st)