-- | controls are widgets that are:
-- * specified in the program text,
-- * displayed in the GUI,
-- * read while executing the program.
module Controls (
module Controls,
module ControlsBase,
) where
import ControlsBase
( Name, deconsName, Assignments,
Value (Bool, Number), Values (boolValues, numberValues) )
import qualified ControlsBase as C
import qualified Program
import qualified Module
import qualified Rule
import qualified Term
import qualified Exception
import qualified Control.Monad.Exception.Synchronous as Exc
import qualified Control.Monad.Trans.Writer as MW
import qualified Control.Monad.Trans.Class as MT
import Control.Monad.IO.Class ( liftIO )
import qualified Graphics.UI.WX as WX
import qualified Graphics.UI.WXCore.WxcClassesMZ as WXCMZ
import Graphics.UI.WX.Attributes ( Prop((:=)), set, get )
import Graphics.UI.WX.Classes ( text, checked, selection )
import Graphics.UI.WX.Events ( on, command, select )
import Graphics.UI.WX.Layout ( layout, container, row, column, widget )
import qualified Data.Map as M
import Data.Foldable ( forM_ )
import Control.Functor.HT ( void )
data Event = Event Name Value
deriving Show
moduleName :: Module.Name
moduleName = Module.Name "Controls"
defltIdent :: Term.Term
defltIdent = read "deflt"
changeControllerModule ::
Program.Program ->
Event ->
Exc.Exceptional Exception.Message Program.Program
changeControllerModule p0 (Event name val) =
fmap (\p -> p{Program.controlValues =
updateValue name val $ Program.controlValues p}) .
flip Program.replaceModule p0 .
Module.addRule ( controllerRule name val ) =<<
Exc.fromMaybe
( Module.inoutExceptionMsg moduleName
"cannot find module for controller updates" )
( M.lookup moduleName $ Program.modules p0 )
updateValue ::
Name -> Value -> Values -> Values
updateValue name val vals =
case val of
Bool b ->
vals{boolValues = M.insert name b $ boolValues vals}
Number x ->
vals{numberValues = M.insert name x $ numberValues vals}
controllerRule ::
Name -> Value -> Rule.Rule
controllerRule name val =
case val of
Bool b ->
Rule.Rule
( read "checkBox" )
[ Term.StringLiteral
( Module.nameRange moduleName )
( deconsName name ),
defltIdent ]
( Term.Node ( read $ show b ) [] )
Number x ->
Rule.Rule
( read "slider" )
[ Term.StringLiteral
( Module.nameRange moduleName )
( deconsName name ),
read "lower",
read "upper",
defltIdent ]
( Term.Number ( Module.nameRange moduleName ) ( fromIntegral x ) )
create ::
WX.Frame () ->
Assignments ->
(Event -> IO ()) ->
IO ()
create frame controls sink = do
size <- WX.get frame WX.outerSize
void $ WXCMZ.windowDestroyChildren frame
panel <- WX.panel frame []
(cs,ss) <- MW.runWriterT $ MW.execWriterT $ forM_ (M.toList controls) $
\ ( name, (_rng, con) ) ->
case con of
C.CheckBox val -> do
cb <- liftIO $ WX.checkBox panel
[ text := deconsName name , checked := val ]
liftIO $ set cb
[ on command := do
c <- get cb checked
sink $ Event name $ Bool c
]
MW.tell [ widget cb ]
C.Slider lower upper val -> do
sl <- liftIO $ WX.hslider panel False lower upper
[ selection := val ]
sp <- liftIO $ WX.spinCtrl panel lower upper
[ selection := val ]
liftIO $ set sl
[ on command := do
c <- get sl selection
set sp [ selection := c ]
sink $ Event name $ Number c
]
liftIO $ set sp
[ on select := do
c <- get sp selection
set sl [ selection := c ]
sink $ Event name $ Number c
]
MT.lift $ MW.tell [
WX.row 5 [ WX.hfill $ widget sl , widget sp,
WX.label (deconsName name) ]
]
set frame [
layout :=
container panel $ column 5 $
WX.hfloatCenter (row 5 cs) : ss,
WX.outerSize := size
]