packages feed

keera-hails-mvc-environment-gtk-0.5.0: src/Hails/MVC/GenericCombinedEnvironment.hs

-- | A combined environment combines a view and a model in a MVC-structured
--   application. The environment can be passed around to the controller, which
--   must update each side accordingly and keep both in sync.
--
--   In this package, the view is built using GTK, and the model is implemented
--   as a 'ProtectedModel' (a safe-thread structure with a fine-grained notion
--   of change).
--
-- Copyright   : (C) Keera Studios Ltd, 2013
-- License     : BSD3
-- Maintainer  : support@keera.co.uk
module Hails.MVC.GenericCombinedEnvironment
    ( -- * Combined Environments (CEnvs)
      CEnv (view, model)
      -- * CEnv construction
    , createCEnv
      -- * CEnv updates
    , installCondition
    , installConditions
    )
  where

-- Internal libraries
import Hails.MVC.Model.ProtectedModel (ProtectedModel, startProtectedModel)
import Hails.MVC.Model.ReactiveModel  (Event)
import Hails.MVC.View                 (createView)
import Hails.MVC.View.GtkView         (GtkGUI, GtkView)

-- | Given a GUI and a type for the events that capture the changes inside the
--   model, a CEnv contains a view and a protected model.
data (GtkGUI a, Event c) => CEnv a b c = CEnv
  { view  :: GtkView a          -- ^ View in the CEnv of an MVC application.
  , model :: ProtectedModel b c -- ^ Model in the CEnv of an MVC application.
  }

-- | Create a combined environment from a given model and initialize the
--   model and the view. The default model must be provided explicitly.
--   The initialisation operations for the view and the protected model
--   are called internally.
createCEnv :: (GtkGUI a, Event c) => b -> IO (CEnv a b c)
createCEnv emptyBM = CEnv <$> createView
                          <*> startProtectedModel emptyBM

-- | Install a condition in a Combined Environment. Conditions, or
--   reactive rules, are used to keep both the model and the view
--   in sync.

-- NOTE: This is an experimental function and might be removed in the future.
installCondition :: (GtkGUI a, Event c)
                 => CEnv a b c
                 -> (CEnv a b c -> IO ())
                 -> IO ()
installCondition cenv cond = cond cenv

-- | Install several conditions in a Combined Environment. Conditions, or
--   reactive rules, are used to keep both the model and the view
--   in sync.

-- FIXME: I really don't like the syntax
--   installConditions cenv
--      [ rv1 =:= rf1
--      , ...
--      ]
--
--   I'd rather use
--   installConditions cenv $ do
--     rv1 =:= rf1
--     rv2 =:= rf2
--     ...
--   Which means that I would have to define a monad.
--
-- NOTE: This is an experimental function and might be removed in the future.
installConditions :: (GtkGUI a, Event c)
                  => CEnv a b c
                  -> [CEnv a b c -> IO ()]
                  -> IO ()
installConditions cenv conds = mapM_ (installCondition cenv) conds