Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Draft] Multiple top-level windows (and some questions) #75

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import qualified ManyBoxes
import qualified MenuBar
import qualified Notebook
import qualified Paned
import qualified Windows

main :: IO ()
main =
Expand All @@ -36,6 +37,7 @@ main =
, ("CSS" , CSS.main)
, ("Paned" , Paned.main)
, ("Dialog" , Dialog.main)
, ("Windows" , Windows.main)
]
in getArgs >>= \case
[example] -> case lookup example examples of
Expand Down
91 changes: 91 additions & 0 deletions examples/Windows.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Windows where

import Control.Concurrent (threadDelay)
import Control.Monad (void)
import Data.Text (pack)
import Data.Vector (Vector)
import qualified Data.Vector as Vector
import Pipes.Prelude (repeatM)

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

type State = Vector (Maybe Int)

data Event
= IncrAll
| AddWindow
| CloseWindow Int
| RemoveWindow
| Closed

view' :: State -> AppView Window Event
view' ns =
bin
Window
[ #title := "Windows"
, on #deleteEvent (const (True, Closed))
, #widthRequest := 200
, #heightRequest := 300
]
$ container Box [#orientation := OrientationVertical, #spacing := 4, #margin := 4]
$ [addButton, removeButton ns]
<> Vector.imap windowLabel ns

addButton :: BoxChild Event
addButton = BoxChild defaultBoxChildProperties
$ widget Button [#label := "Add Window", on #clicked AddWindow]

removeButton :: State -> BoxChild Event
removeButton ns = BoxChild defaultBoxChildProperties $ widget
Button
[ #label := "Remove Window"
, #sensitive := (ns /= mempty)
, on #clicked RemoveWindow
]

windowLabel :: Int -> Maybe Int -> BoxChild Event
windowLabel i n =
BoxChild defaultBoxChildProperties { padding = 4 }
$ windowHost (window i <$> n)
$ windowChild i n

window :: Int -> Int -> Bin Window Event
window i x = bin
Window
[ #title := pack ("Window " <> show i)
, on #deleteEvent (const (True, CloseWindow i))
]
(widget Label [#label := pack ("Open for " <> show x <> " seconds")])

windowChild :: Int -> Maybe Int -> Widget Event
windowChild i = \case
Nothing -> widget Label [#label := pack ("Window " <> show i <> " Closed")]
Just _ -> widget Label [#label := pack ("Window " <> show i <> " Open")]

update' :: State -> Event -> Transition State Event
update' ns = \case
IncrAll -> Transition (fmap succ <$> ns) (return Nothing)
AddWindow -> Transition (ns `Vector.snoc` Just 1) (return Nothing)
CloseWindow i -> Transition
(Vector.imap (\i' n -> if i' == i then Nothing else n) ns)
(return Nothing)
RemoveWindow -> Transition (Vector.init ns) (return Nothing)
Closed -> Exit

main :: IO ()
main = void $ run App
{ view = view'
, update = update'
, inputs = [incrPeriodically]
, initialState = []
}
where incrPeriodically = repeatM $ IncrAll <$ threadDelay (1000 * 1000)
1 change: 1 addition & 0 deletions examples/examples.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ executable example
, MenuBar
, Notebook
, Paned
, Windows
build-depends: base >=4.10 && <5
, async
, bytestring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ runLoop App {..} = do
sub <- subscribe newMarkup newState (publishEvent events)
return (newState, sub)
Replace createNew -> runUI $ do
Gtk.widgetDestroy =<< someStateWidget oldState
destroy oldState oldMarkup
cancel oldSubscription
newState <- createNew
Gtk.widgetShowAll =<< someStateWidget newState
Expand Down
1 change: 1 addition & 0 deletions gi-gtk-declarative/gi-gtk-declarative.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ library
, GI.Gtk.Declarative.State
, GI.Gtk.Declarative.Widget
, GI.Gtk.Declarative.Widget.Conversions
, GI.Gtk.Declarative.WindowHost
build-depends: base >=4.10 && <5
, containers >= 0.6 && < 0.7
, data-default-class >= 0.1 && <0.2
Expand Down
1 change: 1 addition & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ import GI.Gtk.Declarative.Widget as Export
import GI.Gtk.Declarative.Widget.Conversions
as Export
( )
import GI.Gtk.Declarative.WindowHost as Export
10 changes: 9 additions & 1 deletion gi-gtk-declarative/src/GI/Gtk/Declarative/Bin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ instance (Gtk.IsBin parent) => Patchable (Bin parent) where
case patch oldChildState oldChild newChild of
Modify modify -> SomeState . StateTreeBin top' <$> modify
Replace createNew -> do
Gtk.widgetDestroy =<< someStateWidget oldChildState
destroy oldChildState oldChild
newChildState <- createNew
childWidget <- someStateWidget newChildState
Gtk.widgetShow childWidget
Expand All @@ -118,6 +118,14 @@ instance (Gtk.IsBin parent) => Patchable (Bin parent) where
else Replace (create (Bin ctor newAttributes newChild))
_ -> Replace (create (Bin ctor newAttributes newChild))

destroy (SomeState st) (Bin _ _ child) = do
case st of
StateTreeBin node childState -> do
destroy childState child
Gtk.toWidget (stateTreeWidget node) >>= Gtk.widgetDestroy
_ ->
error "Bin destroy method called with non-StateTreeBin state"

--
-- EventSource
--
Expand Down
8 changes: 8 additions & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/Container.hs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ instance
(unChildren newChildren)
else Replace (create new)
_ -> Replace (create new)

destroy (SomeState (st :: StateTree stateType w c e cs)) (Container _ _ (children :: Children child e2)) = do
case st of
StateTreeContainer top childStates -> do
sequence_ (Vector.zipWith destroy childStates (unChildren children))
Gtk.toWidget (stateTreeWidget top) >>= Gtk.widgetDestroy
_ ->
error "Container destroy method called with non-StateTreeContainer state"

--
-- EventSource
Expand Down
1 change: 1 addition & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/Container/Box.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ instance Patchable BoxChild where
create = create . child
patch s b1 b2 | properties b1 == properties b2 = patch s (child b1) (child b2)
| otherwise = Replace (create b2)
destroy s b = destroy s (child b)

instance EventSource BoxChild where
subscribe BoxChild {..} = subscribe child
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ instance Patchable GridChild where
create = create . child
patch s b1 b2 | properties b1 == properties b2 = patch s (child b1) (child b2)
| otherwise = Replace (create b2)
destroy s b = destroy s (child b)

instance EventSource GridChild where
subscribe GridChild {..} = subscribe child
Expand All @@ -68,4 +69,3 @@ instance IsContainer Gtk.Grid GridChild where
replaceChild grid gridChild' _i old new = do
Gtk.widgetDestroy old
appendChild grid gridChild' new

10 changes: 9 additions & 1 deletion gi-gtk-declarative/src/GI/Gtk/Declarative/Container/MenuItem.hs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ menuItem
-> MenuItem event
menuItem item attrs = MenuItem . Bin item attrs

-- | Construct a sub menu for a 'Gtk.Menu', wit a text label and the
-- | Construct a sub menu for a 'Gtk.Menu', with a text label and the
-- child menu items.
subMenu :: Text -> Vector (MenuItem event) -> MenuItem event
subMenu label = SubMenu label . container Gtk.Menu mempty
Expand Down Expand Up @@ -104,6 +104,14 @@ instance Patchable MenuItem where
-- TODO: case for l1 /= l2
_ -> Replace (create (SubMenu l2 c2))
patch _ _ b2 = Replace (create b2)
destroy state (MenuItem (c :: Bin i e)) =
destroy state c
destroy (SomeState st) (SubMenu _ c) = case st of
StateTreeBin top childState -> do
destroy childState c
Gtk.toWidget (stateTreeWidget top) >>= Gtk.widgetDestroy
_ ->
error "Cannot destroy SubMenu with non-StateTreeBin state"

instance EventSource MenuItem where
subscribe (MenuItem item ) state cb = subscribe item state cb
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pane paneProperties paneChild = Pane { .. }
instance Patchable Pane where
create = create . paneChild
patch s b1 b2 = patch s (paneChild b1) (paneChild b2)
destroy s b = destroy s (paneChild b)

instance EventSource Pane where
subscribe Pane {..} = subscribe paneChild
Expand Down
7 changes: 5 additions & 2 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/Container/Patch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ patchInContainer (StateTreeContainer top children) container os' ns' = do
-- 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
error "this shouldn't happen!"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to call destroy here with oldChildState, but we can't do that because destroy also requires the old declarative widget - which we don't have.

Does this situation ever actually happen? It seems to me that if oldChildState exists then we should also have the old declarative widget (since that would have been used to create oldChildState).

{-
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.
Expand All @@ -92,8 +95,8 @@ patchInContainer (StateTreeContainer top children) container os' ns' = do

-- When a declarative widget has been removed, remove the GTK widget from
-- the container.
(_i, Just childState, Just _, Nothing) -> do
Gtk.widgetDestroy =<< someStateWidget childState
(_i, Just oldChildState, Just old, Nothing) -> do
destroy oldChildState old
return Vector.empty

-- When there are more old declarative widgets than GTK widgets, we can
Expand Down
3 changes: 3 additions & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/CustomWidget.hs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ instance
)
| otherwise -> Replace (create new)
_ -> Replace (create new)

destroy state _custom =
someStateWidget state >>= Gtk.widgetDestroy

instance
(Typeable internalState, Gtk.GObject widget) =>
Expand Down
2 changes: 2 additions & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/Patch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ class Patchable widget where
-- | Given two declarative widgets of the same widget type (but not
-- necessarily of the same event types,) calculate a 'Patch'.
patch :: SomeState -> widget e1 -> widget e2 -> Patch
-- | Given a previously created declarative widget, destroy it.
destroy :: SomeState -> widget e -> IO ()
2 changes: 2 additions & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/SingleWidget.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ instance Patchable (SingleWidget widget) where
)
else Replace (create (SingleWidget ctor newAttributes))
_ -> Replace (create (SingleWidget ctor newAttributes))
destroy state _ =
someStateWidget state >>= Gtk.widgetDestroy

instance EventSource (SingleWidget widget) where
subscribe (SingleWidget (_ :: Gtk.ManagedPtr w1 -> w1) props) (SomeState (st :: StateTree
Expand Down
1 change: 1 addition & 0 deletions gi-gtk-declarative/src/GI/Gtk/Declarative/Widget.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ instance Patchable Widget where
patch s (Widget (w1 :: t1 e1)) (Widget (w2 :: t2 e2)) = case eqT @t1 @t2 of
Just Refl -> patch s w1 w2
_ -> Replace (create w2)
destroy s (Widget w) = destroy s w

instance EventSource Widget where
subscribe (Widget w) = subscribe w
Expand Down
Loading