packages feed

frpnow-gtk3-0.2.0: Control/FRPNow/GTK/Containers.hs

{- |
Module      :  Control.FRPNow.GTK.Containers
Copyright   :  (c) George Steel 2017
License     :  BSD3
Maintainer  :  george.steel@gmail.org

EDSL for widget layouts ussing GTK container widgets.

Containers containing single children are created by actions taking the child widget as their last parameter, allowing for easy composition using '=<<'.
Containers taking multiple children take their contents as a reader monad which runs over the newly-created widget, allowing packing commands to insert children of heterogeneous types, possibly with options.
As all container-creation actions run in 'MonadIO', nesting containers can be done inline with standard monadic composition.

Given the wiegets foo, bar, baz, where foo supports scrolling (such as a TreeView) we can create a simple layout as follows

> layout <- createVBox 0 $ do
>     bstretch =<< createFrame ShadowIn =<< createScrolledWindow foo
>     bpack <=< createHBox 0 $ do
>         bpack bar
>         bspacer
>         bpack =<< set' [attr := val] baz

-}

module Control.FRPNow.GTK.Containers where

import Graphics.UI.Gtk
import Control.Applicative
import Control.Monad
import Control.Monad.Reader
import Control.Monad.IO.Class
import qualified Data.Text as T
import Data.Text (Text)

-- | Destroys all children in a container, leaving it empty
clearChildren :: (MonadIO m, ContainerClass w) => w -> m ()
clearChildren cont = liftIO $ do
    children <- containerGetChildren cont
    forM_ children widgetDestroy

-- | Sets aattributes on a widget and returns the widget. Useful for setting attributes inline in a composition chain.
set' :: (MonadIO m) => [AttrOp w] -> w -> m w
set' ops w = liftIO $ do
    set w ops
    return w

-- * Box

-- | Creates an HBox with a given spacing and fills it using the reader.
createHBox :: (MonadIO m) => Int -> ReaderT HBox IO a -> m HBox
createHBox spacing filler = liftIO $ do
    b <- hBoxNew False spacing
    runReaderT filler b
    return b

-- | Creates a VBox with a given spacing and fills it using the reader.
createVBox :: (MonadIO m) => Int -> ReaderT VBox IO a -> m VBox
createVBox spacing filler = liftIO $ do
    b <- vBoxNew False spacing
    runReaderT filler b
    return b

-- | Inserts a witget into the enclosing box as its natural size.
bpack :: (WidgetClass w, BoxClass b) => w -> ReaderT b IO ()
bpack w = ReaderT $ \b -> boxPackStart b w PackNatural 0

-- | Inserts a widget into the enclosing box with rubber length (takes all excess space).
bstretch :: (WidgetClass w, BoxClass b) => w -> ReaderT b IO ()
bstretch w = ReaderT $ \b -> boxPackStart b w PackGrow 0

-- | Inserts an expanding spacer into a box
bspacer :: (BoxClass b) => ReaderT b IO ()
bspacer = ReaderT $ \b -> do
    s <- hBoxNew False 0
    boxPackStart b s PackGrow 10


-- * Grid

-- | Creates a Grid (with the given x and y spacing) and fills it using the reader.
createGrid :: (MonadIO m) => Int -> Int -> ReaderT Grid IO () -> m Grid
createGrid xspace yspace filler = liftIO $ do
    g <- gridNew
    gridSetRowSpacing g yspace
    gridSetColumnSpacing g xspace
    runReaderT filler g
    return g

-- | Inserts a widget into the enclosing Grid at the given (x,y) position.
gcell :: (WidgetClass w, GridClass g) => (Int,Int) -> w -> ReaderT g IO ()
gcell (x,y) w = ReaderT $ \g -> gridAttach g w x y 1 1

-- | Inserts a widget into the enclosign Grid spanning several cells, with position fiven by the first srgument and size given by the second.
gcellspan :: (WidgetClass w, GridClass g) => (Int,Int) -> (Int,Int) -> w -> ReaderT g IO ()
gcellspan (x,y) (dx,dy) w = ReaderT $ \g -> gridAttach g w x y dx dy


-- * Stack

-- | Creates a Stack and fills it using the reader.
createStack :: (MonadIO m) => ReaderT Stack IO () -> m Stack
createStack filler = liftIO $ do
    s <- stackNew
    runReaderT filler s
    return s

-- | Adds an element to the enclosing Stack with the given key.
stackElem :: (WidgetClass w) => Text -> w -> ReaderT Stack IO ()
stackElem key w = ReaderT $ \s -> stackAddNamed s w key

-- | Adds an element to the enclosing Stack with the given key and title.
stackElemTitled :: (WidgetClass w) => Text -> Text -> w -> ReaderT Stack IO ()
stackElemTitled key title w = ReaderT $ \s -> stackAddTitled s w key title

-- | Creates a StackSwitcher for a given Stack
createStackSwitcher :: (MonadIO m) => Stack -> m StackSwitcher
createStackSwitcher s = liftIO $ do
    sw <- stackSwitcherNew
    stackSwitcherSetStack sw s
    return sw


-- * Notebook

-- | Createa a Notebook and fills it using the reader.
createNotebook :: (MonadIO m) => ReaderT Notebook IO a -> m Notebook
createNotebook filler = liftIO $ do
    s <- notebookNew
    runReaderT filler s
    return s

-- | Add a tab to the enclosing Notebook with the given title
nbpage :: (WidgetClass w) => Text -> w -> ReaderT Notebook IO ()
nbpage lbl w = ReaderT $ \nb -> void (notebookAppendPage nb w lbl)


-- * Scrolling

-- | Creates a ScrolledWindow around a widget supporting scrolling natively.
createScrolledWindow :: (MonadIO m, WidgetClass w) => w -> m ScrolledWindow
createScrolledWindow w = liftIO $ do
    scr <- scrolledWindowNew Nothing Nothing
    containerAdd scr w
    return scr

-- | Creates a ScrolledWindow and Vieqwport around a widget not supporting scrollign such as a Box or Grid.
createScrolledViewport :: (MonadIO m, WidgetClass w) => w -> m ScrolledWindow
createScrolledViewport w = liftIO $ do
    scr <- scrolledWindowNew Nothing Nothing
    scrolledWindowAddWithViewport scr w
    return scr


-- * Misc

-- | Creates a frame around a widget with a given shadow type
createFrame :: (MonadIO m, WidgetClass w) => ShadowType -> w -> m Frame
createFrame shad w = liftIO $ do
    f <- frameNew
    frameSetShadowType f shad
    containerAdd f w
    return f

-- | Creates an Expander around a given widget. The Text and Bool parameters control the label of the expander and whether it starts expanded.
createExpander :: (MonadIO m, WidgetClass w) => Text -> Bool -> w -> m Expander
createExpander lbl startpos w = liftIO $ do
    ex <- expanderNew lbl
    expanderSetExpanded ex startpos
    containerAdd ex w
    return ex

-- | Creates an HPaned contsing two other widgets.
createHPaned :: (MonadIO m, WidgetClass w1, WidgetClass w2) => w1 -> w2 -> m HPaned
createHPaned l r = liftIO $ do
    p <- hPanedNew
    panedPack1 p l True False
    panedPack2 p r True False
    return p
-- | Creates a VPaned containing two other widgets.
createVPaned :: (MonadIO m, WidgetClass w1, WidgetClass w2) => w1 -> w2 -> m VPaned
createVPaned l r = liftIO $ do
    p <- vPanedNew
    panedPack1 p l True False
    panedPack2 p r True False
    return p