Notebook

The Notebook widget is a collection of "pages" that overlap each other. Each page is different, and only one will be visible at a time. Pages 'contain' some other widget, which you must supply.

To create a new notebook widget:

NotebookNew :: IO Notebook

Once the notebook has been created, there are a number of functions and attributes to use and to customize it.The following attributes determine the position of the tabs, and whether they are visible at all.

notebookTabPos :: NotebookClass self => Attr self PositionType
notebookShowTabs :: NotebookClass self => Attr self Bool

The PositionType has constructors PosLeft , PosRight , PosTop (the default) and PosBottom.

Next we will look at how to add pages to the notebook. There are three ways, append, prepend and insert.

noteBookAppendPage :: (NotebookClass self, WidgetClass child)
=> self
-> child         -- the widget to use as the contents of the page
-> String        -- the label text
-> IO Int        -- the index (page number) of the new page (starting at 0)

The function notebookPrependPage has exactly the same signature. It will, of course, return 0 as its index. The function notebookInsertPage takes the index at which to insert the page as an additional parameter. You can remove pages with notebookRemovePage.

A Notebook is a container widget and you can use other containers as a child, including horizontal and vertical boxes. Therefore you can build quite complex pages, and set their layouts with the usual packing functions.

The listed functions to add, prepend and insert pages only work with textual tabs. All three also have versions which allow for a popup menu to appear, and with those you can use any widget as a label.

notebookAppendPageMenu ::
(NotebookClass self, WidgetClass child, WidgetClass tabLabel, WidgetClass menuLabel)
=> self
-> child           -- the widget to use as the contents of the page
-> tabLabel        -- the widget to use as the label of the page
-> menuLabel       -- the widget to use as the label of the popup menu
-> IO Int          -- the index (page number) of the new page (starting at 0)

notebookPrependPageMenu and notebookInsertPageMenu will place the new page first or at the designated index, respectively.

Some useful attributes (but see the API Documentation for more) are:

notebookScrollable :: NotebookClass self => Attr self Bool
notebookCurrentPage :: NotebookClass self => Attr self Int
notebookEnablePopup :: NotebookClass self => Attr self Bool

If the number of pages is large you can set notebookScrollable . Use notebookCurrentPage or the function notebookSetCurrentPage to open the notebook at another page than the (default) first one. The attribute notebookEnablePopup determines whether the user's clicking the right mouse button on a tab will show a popup menu of all the available pages. That is, if the menu fuctions have been defined.

A Notebook widget has its own signal handling function:

onSwitchPage :: NotebookClass nb => nb -> (Int -> IO ()) -> IO (ConnectId nb)

The function you must supply takes a page index returned by onSwitchPage and must do some output.

The examples show a catalogue of StockItem icon sets in different ways.

Notebook Example 1

Stock items were summarily discussed in chapter 4.5. Recall that a StockItem is known throughout GTK+ (and Gtk2Hs). The following function produces a list of all stock identifiers.

stockListIds :: IO [StockId]

A StockId is a String and in Gtk2Hs has the form: stockCopy , stockDialogError etc. In GTK+ the corresponding form is: gtk-copy, gtk-dialog-error and so on. The example defines a function tabName to convert the GTK+ identifiers in the StockId list to names for the notebook tabs. The function myNewPage uses imageNewFromStock to get the icon into an Image widget, which is then added to the page. It returns the page index, but this is not used. To get a list of all pages use sequence instead of sequence_

Note that the icon size, in pixels, may be restricted. The default is 4 and the value used here, 6, is also allowed but a size of 8 produces a run time error with GHCi.

import Graphics.UI.Gtk
import Data.Char (toUpper)

main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Notebook Example 1", windowDefaultWidth := 300,
                 windowDefaultHeight := 200 ]
     
     ntbk <- notebookNew
     containerAdd window ntbk
     set ntbk [notebookScrollable := True, notebookTabPos := PosBottom]

     stls <- stockListIds
     sequence_ (map (myNewPage ntbk) stls)

     onSwitchPage ntbk (putStrLn . ((++)"Page: ") . show)

     widgetShowAll window
     onDestroy window mainQuit
     mainGUI

tabName :: StockId -> String
tabName st = (drop 3) (conv st) where
                  conv (x:[]) = x:[]
                  conv (x:y:ys) | x == '-' = (toUpper y):(conv ys)
                                | otherwise = x: (conv (y:ys))

myNewPage :: Notebook -> StockId -> IO Int
myNewPage noteb stk = 
          do img <- imageNewFromStock stk 6
             pagenum <- notebookAppendPage noteb img (tabName stk)
             return pagenum          

A second way to show the catalogue is to put the icons in the notebook tabs.

Notebook Example 2

To do this we need the menu style of adding pages, and we've also defined a menu tab consisting of the first letter of the name string. The result is a popup menu consisting of 98 letters, which itself will scroll. It can simply be disabled through the notebookEnablePopup attribute. The content of each page is the Gtk2Hs stock item identifier (see Graphics.UI.Gtk.General.StockItems).

import Graphics.UI.Gtk
import Data.Char (toUpper)

main :: IO ()
main= do
     initGUI
     window <- windowNew
     set window [windowTitle := "Notebook Example 2", windowDefaultWidth := 300,
                 windowDefaultHeight := 200 ]
     
     ntbk <- notebookNew
     containerAdd window ntbk
     set ntbk [notebookScrollable := True, notebookEnablePopup := True,
                    notebookTabPos := PosRight ]

     stls <- stockListIds
     sequence_ (map (myNewPage ntbk) stls)

     onSwitchPage ntbk (putStrLn . ((++)"Page: ") . show)

     widgetShowAll window
     onDestroy window mainQuit
     mainGUI

tabName :: StockId -> String
tabName st = (drop 3) (conv st) where
                  conv (x:[]) = x:[]
                  conv (x:y:ys) | x == '-' = (toUpper y):(conv ys)
                                | otherwise = x: (conv (y:ys))

myNewPage :: Notebook -> StockId -> IO Int
myNewPage noteb stk = 
          do img <- imageNewFromStock stk 4
             let nmstr = tabName stk
             men <- labelNew (Just ((take 1) nmstr))
             cont <- labelNew (Just ("stock" ++ nmstr))
             pagenum <- notebookAppendPageMenu noteb cont img men
             return pagenum