Closed zenhack closed 4 years ago
OK! I haven't used the notebook widget, but I see how this is problematic.
Would the existing solution for menus be helpful, in https://github.com/owickstrom/gi-gtk-declarative/blob/master/gi-gtk-declarative/src/GI/Gtk/Declarative/Container/MenuItem.hs? Menus are simpler though, they have the same type of children. Maybe it's better if you create a custom widget wrapper for it, it seems hard to fit into the "happy path" of widget/bin/container, as you say. And extending that model just for one type of widget isn't very appealing.
I want to use a Notebook, but I don't need the fancy stuff: just labelled tabs. I was able to get this working fairly easily by looking at the other containers - see below:
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Notebook where
import Control.Monad (void)
import Data.Text (Text)
import Data.Vector (Vector)
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative.Container.Class
import GI.Gtk.Declarative.EventSource
import GI.Gtk.Declarative.Widget
import GI.Gtk.Declarative.Patch
data NotebookChild event = NotebookChild
{ label :: Text
, child :: Widget event
}
deriving (Functor)
instance Patchable NotebookChild where
create = create . child
patch s b1 b2 = patch s (child b1) (child b2)
instance EventSource NotebookChild where
subscribe NotebookChild{..} = subscribe child
instance ToChildren Gtk.Notebook Vector NotebookChild
instance IsContainer Gtk.Notebook NotebookChild where
appendChild notebook NotebookChild{label} widget = do
lbl <- Gtk.labelNew (Just label)
void $ Gtk.notebookAppendPage notebook widget (Just lbl)
replaceChild notebook NotebookChild{label} i old new = do
Gtk.widgetDestroy old
lbl <- Gtk.labelNew (Just label)
void $ Gtk.notebookInsertPage notebook new (Just lbl) i
I can send a pull request with docs and examples, if this is something you want in the library.
I managed to get tab labels to be arbitrary widgets. I'm not 100% sure this is safe, but it works as far as I have tested it:
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Gi.Gtk.Declarative.Notebook
( Page
, page
, pageWithTab
, notebook
) where
import Control.Monad (void)
import Data.Maybe (isNothing)
import Data.Text (Text, pack)
import Data.Vector (Vector)
import qualified Data.Vector as Vector
import GHC.Ptr (nullPtr)
import qualified GI.GLib as GLib
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative
import GI.Gtk.Declarative.Container
import GI.Gtk.Declarative.Container.Class
data Page event = Page
{ tabLabel :: Widget event
, child :: Widget event
}
page :: Text -> Widget event -> Page event
page label = pageWithTab (widget Gtk.Label [#label := label])
pageWithTab :: Widget event -> Widget event -> Page event
pageWithTab = Page
notebook :: Vector (Attribute Gtk.Notebook event) -> Vector (Page event) -> Widget event
notebook attrs children =
let tabsAndChildren = concat $ (\Page{..} -> [child, tabLabel]) <$> children
in container Gtk.Notebook attrs $ Vector.fromList tabsAndChildren
instance ToChildren Gtk.Notebook Vector Widget
instance IsContainer Gtk.Notebook Widget where
appendChild parent _ new = do
lastPage <- Gtk.notebookGetNthPage parent (-1)
case lastPage of
Nothing -> do -- this is the first page to be added
void $ Gtk.notebookAppendPage parent new (Nothing :: Maybe Gtk.Widget)
Just p -> do
label <- Gtk.notebookGetTabLabel parent p
if isNothing label then -- this page must already have a child, we just need to set the label
Gtk.notebookSetTabLabel parent p (Just new)
else -- the last page has a child and a label, so create a new page instead
void $ Gtk.notebookAppendPage parent new (Nothing :: Maybe Gtk.Widget)
replaceChild parent _ i old new = do
let i' = i `div` 2
pageI <- Gtk.notebookGetNthPage parent i'
case pageI of
Nothing -> do
GLib.logDefaultHandler
(Just "gi-gtk-declarative")
[GLib.LogLevelFlagsLevelError]
(Just $ "Notebook.replaceChild called with an index where there is no child: " <> pack (show i))
nullPtr
Just p -> do
if i `mod` 2 == 0 then do -- we have to replace the child
label <- Gtk.notebookGetMenuLabel parent p
Gtk.widgetDestroy old
void $ Gtk.notebookInsertPage parent new label i'
else do -- we have to replace the label
Gtk.notebookSetTabLabel parent p (Just new)
Usage:
notebook []
[ page "First Page" (widget ...)
, page "Second Page" (widget ...)
]
@Dretch I'm unsure about the Notebook API, specifically, but from a gi-gtk-declarative point of view (if that makes sense), your code looks good. :+1:
Now that it's generic, would you mind submitting this wrapper in a PR?
Closing this now, as @Dretch submitted a PR that's merged and released on Hackage as 0.6.2.
I found myself wanting to use a gtk notebook (tabs), but it looks like this widget will need a little bit of custom logic to be able to add children.
I tried adding the necessary instances myself, but here's where I got stuck:
StateTree
seems to assume all parent/child relationships fit into the widget/bin/container ontology, but notebooks have "pages" which have a main contents widget, but also the tab itself can be an arbitrary widget, and there's also a "menu label" widget that can be specified. Furthermore, there is no widget caled "page"; the relevant functions just take more than one widget when adding in the same call. So I don't know how to construct theSomeState
for a notebook, or write theEventSource
andPatchable
instances.Any insights?
Relevant gtk docs:
https://developer.gnome.org/gtk3/stable/GtkNotebook.html#gtk-notebook-append-page