phooey-1.2: src/Graphics/UI/Phooey/Imperative.hs
{-# OPTIONS #-}
----------------------------------------------------------------------
-- |
-- Module : Graphics.UI.Phooey.Imperative
-- Copyright : (c) Conal Elliott 2006
-- License : LGPL
--
-- Maintainer : conal@conal.net
-- Stability : provisional
-- Portability : portable
--
-- Some imperative UI tools.
----------------------------------------------------------------------
module Graphics.UI.Phooey.Imperative
(
-- * Widget & layout tools
hwidget,lhwidget ,hsliderDyn, mkNews, commandNews
-- * Simple abstraction around widget containers and frames
, Win, Wio, runWio
)
where
import Graphics.UI.WX
import Graphics.UI.WXCore (sliderGetMin,sliderGetMax,sliderSetRange)
import Data.IORef
import Control.Monad (join)
import Data.Monoid (mappend)
import Control.Instances () -- For Monoid (IO a) instance
{-----------------------------------------------------------------------
Widget & layout tools
-----------------------------------------------------------------------}
-- -- | Get the current 'selection' value of a widget
-- getSel :: Selection ctl => ctl -> IO Int
-- getSel ctl = get ctl selection
-- -- | Set a single attribute
-- set1 :: w -> Attr w a -> a -> IO ()
-- set1 w attr val = set w [ attr := val ]
-- -- | Set the 'command' callback
-- onCommand :: Commanding ctl => ctl -> IO () -> IO ()
-- onCommand ctl io = set ctl [ on command := io ]
-- | Horizontally-filled widget layout
hwidget :: Widget w => w -> Layout
hwidget = hfill . widget
-- | Labeled, horizontally-filled widget layout
lhwidget :: Widget w => String -> w -> Layout
lhwidget str = boxed str . hwidget
-- | Dynamically bounded slider. The main complication is keeping the
-- slider value within the dynamic bounds.
hsliderDyn :: Window a -> Bool -> [Prop (Slider ())]
-> IO (Slider (), (Int,Int) -> IO ())
hsliderDyn win showBounds props =
do -- The reason for +- 100 in |makeISlider| is simply to reserve
-- space. There's a wxWidgets (I think) oddity that requires manual
-- resizing otherwise.
ctl <- hslider win showBounds (-100) (100) props
return (ctl, setBounds ctl)
where
setBounds ctl (lo',hi') =
do sliderSetRange ctl lo' hi'
val <- get ctl selection
when (val < lo') (setVal ctl lo')
when (val > hi') (setVal ctl hi')
setVal ctl x =
do lo <- sliderGetMin ctl
hi <- sliderGetMax ctl
when (lo <= x && x <= hi)
(set ctl [ selection := x ])
-- | Make a \"news\" publisher given a function that (destructively) assigns
-- a single action to be executed upon some event. The publisher can
-- remember any number of actions (the "subscribers").
mkNews :: (IO () -> IO ()) -> IO (IO () -> IO ())
mkNews setNotify =
do ref <- newIORef (return ()) -- contains subscribed actions
setNotify (join (readIORef ref))
return $ modifyIORef ref . mappend
-- | Make a \"news\" publisher for a widget's command.
commandNews :: Commanding wid => wid -> IO (IO () -> IO ())
commandNews wid = mkNews (\ act -> set wid [ on command := act ])
-- cmdNews wid =
-- do -- putStrLn "mkNews"
-- ref <- newIORef (return ()) -- contains subscribed actions
-- set wid [ on command := join (readIORef ref) ]
-- return $ modifyIORef ref . mappend
-- TODO: perform action only when the value actually changes. I get an
-- extra kick on grabbing a slider and two extra on releasing. Is there a
-- way to eliminate them?
{-----------------------------------------------------------------------
Wio -- simple abstraction around widget containers and frames
-----------------------------------------------------------------------}
type Win = Panel () -- ^ Container of widgets
type Wio = Win -> IO Layout -- ^ Consumes container and yield layout
-- | Run a 'Wio': handle frame & widget creation, and apply layout.
runWio :: String -> Wio -> IO ()
runWio name wio = start $
do f <- frame [ visible := False, text := name ]
win <- panel f []
l <- wio win
set win [ layout := l ]
set f [ layout := hwidget win, visible := True ]