-- | controllers are widgets that are: -- * specified in the program text, -- * displayed in the GUI, -- * read while executing the program. module Controller ( Assignments, Event, create, changeControllerModule, ) where import qualified ControllerBase as C import qualified Program import qualified Module import qualified Rule import qualified Term import qualified Exception import ControllerBase ( Name, deconsName, Assignments, Value (Bool, Number), Values (boolValues, numberValues) ) import Program (Program) import SourceText ( ModuleRange, emptyModuleRange ) import qualified Control.Monad.Exception.Synchronous as ME 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 Map import Data.Foldable ( forM_ ) import Control.Functor.HT ( void ) data Event = Event Name Value deriving Show moduleName :: Module.Name moduleName = Module.Name "Controller" defltIdent :: Term.Term ModuleRange defltIdent = Term.variable moduleName "deflt" changeControllerModule :: Program -> Event -> Exception.Monad 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 ) =<< ME.fromMaybe ( Exception.messageInOutEditor moduleName "cannot find module for controller updates" ) ( Map.lookup moduleName $ Program.modules p0 ) updateValue :: Name -> Value -> Values -> Values updateValue name val vals = case val of Bool b -> vals{boolValues = Map.insert name b $ boolValues vals} Number x -> vals{numberValues = Map.insert name x $ numberValues vals} controllerRule :: Name -> Value -> Rule.Rule ModuleRange controllerRule name val = case val of Bool b -> Rule.Rule ( Term.identifier moduleName "checkBox" ) [ Term.StringLiteral ( emptyModuleRange moduleName ) ( deconsName name ), defltIdent ] ( Term.variable moduleName $ show b ) Number x -> Rule.Rule ( Term.identifier moduleName "slider" ) [ Term.StringLiteral ( emptyModuleRange moduleName ) ( deconsName name ), Term.variable moduleName "lower", Term.variable moduleName "upper", defltIdent ] ( Term.Number ( emptyModuleRange 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_ (Map.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 ]