spike-0.3: src/NotebookSimple.hs
{-# LANGUAGE ViewPatterns #-}
-- this module is a simple reimplementation of GTK Notebook widget.
-- I needed to be able to change the list of pages without receving a signal from it.
module NotebookSimple where
import Graphics.UI.Gtk
import Control.Concurrent
import Control.Concurrent.STM
import Data.List (elemIndex)
import Utils
import Datatypes
data NotebookSimple = NotebookSimple { ns_tabs :: HBox
, ns_widget :: Widget
, ns_pages :: TVar [Page]
, ns_currentPage :: TVar (Maybe Int)
, ns_refresh :: IO ()
}
viewPagesSimpleNotebook :: [Page] -> NotebookSimple -> IO ()
viewPagesSimpleNotebook pages nb = do
atomically $ writeTVar (ns_pages nb) pages
ns_refresh nb
newScrolledWindowWithViewPort :: WidgetClass child => child -> IO ScrolledWindow
newScrolledWindowWithViewPort child = do
sw <- scrolledWindowNew Nothing Nothing
scrolledWindowSetPolicy sw PolicyAlways PolicyNever
scrolledWindowAddWithViewport sw child
(wx,wy) <- widgetGetSizeRequest child
widgetSetSizeRequest sw (wx+5) (wy+40)
return sw
notebookSimpleNew :: (Page -> IO ()) -> IO NotebookSimple
notebookSimpleNew focusOnPage = do
buttonsBox <- hBoxNew False 1
contentBox <- hBoxNew False 1
vbox <- vBoxNew False 1
widgetSetSizeRequest buttonsBox (-1) 10
(\sw -> boxPackStart vbox sw PackNatural 1) =<< newScrolledWindowWithViewPort buttonsBox
boxPackStart vbox contentBox PackGrow 1
widgetShowAll vbox
pgs <- newTVarIO [] :: IO (TVar [Page])
curr <- newTVarIO Nothing
let updateButtons = postGUIAsync $ do
pages <- readTVarIO pgs
listPagesBox focusOnPage pages buttonsBox
updatePage = do
wg <- getCurrentWidget
case wg of
Just wg' -> postGUIAsync (do
containerForeach contentBox (containerRemove contentBox)
set contentBox [ containerChild := (pgWidget wg') ] >> widgetShowAll vbox)
Nothing -> print "strange stuff."
getCurrentWidget = do
c <- readTVarIO curr
p <- readTVarIO pgs
case c of
Nothing -> return Nothing
Just c' -> return (if c' < length p then Just (p !! c') else Nothing)
let watchdog page = do
page' <- waitTVarChangeFrom page curr
updatePage
watchdog page'
let watchdog2 pages = do
pages' <- waitTVarChangeFrom pages pgs
updateButtons
watchdog2 pages'
forkIO (watchdog2 =<< readTVarIO pgs)
forkIO (watchdog =<< readTVarIO curr)
let updateAll = updateButtons >> updatePage
return (NotebookSimple { ns_tabs = contentBox
, ns_widget = (toWidget vbox)
, ns_pages = pgs
, ns_currentPage = curr
, ns_refresh = updateAll })
notebookSimpleAddPage :: NotebookSimple -> Page -> IO ()
notebookSimpleAddPage ns@(ns_pages -> pages) page = do
atomically $ do
pages' <- readTVar pages
writeTVar pages (pages' ++ [page])
notebookSimpleSelectPageIfNone ns
notebookSimpleSelectPage :: NotebookSimple -> Page -> IO ()
notebookSimpleSelectPage ns page = do
atomically $ do
current <- readTVar (ns_currentPage ns)
pages <- readTVar (ns_pages ns)
writeTVar (ns_currentPage ns) (elemIndex page pages)
notebookSimpleSelectPageIfNone :: NotebookSimple -> IO ()
notebookSimpleSelectPageIfNone ns = do
atomically $ do
current <- readTVar (ns_currentPage ns)
case current of
Nothing -> writeTVar (ns_currentPage ns) (Just 0)
Just _ -> return ()
-- | list pages in a box
-- listPagesBox :: (BoxClass box) => [Page] -> box -> IO ()
listPagesBox :: BoxClass box => (Page -> IO a) -> [Page] -> box -> IO ()
listPagesBox focusOnPage pages box = do
containerForeach box (containerRemove box)
mapM_ (\p -> do
title <- getPageTitle p
l <- labelNew (Just title) -- TODO: use AccelLabel and shortcuts for specific pages
labelSetEllipsize l EllipsizeEnd
labelSetWidthChars l 20
labelSetSingleLineMode l True
b <- buttonNew
containerAdd b l
boxPackStart box b PackNatural 1
on b buttonActivated $ do
print ("SIGNAL: on focus", title)
focusOnPage p
return ()
) pages
widgetShowAll box
return ()