I am writing a small RTS game in Haskell, and the time has come to create some GUI for it (buttons, menus and such). However, I don't have any experience in GUI toolkit creation, so I would be thankful for ideas on how to choose right data types, event handling routines and such.
Desired functionality: menu screens (main, load/save, settings, network game), text inputs, game interface (main screen (C&C-like) with buttons, selects and checkboxes.
My current approach is to represent the whole GUI as a state machine that has a number of states and transitions between them that are triggered when an event arrives:
-- Local client state. Actual game state is stored on the server
-- and is updated/synced separately.
data ClientState = […]
-- Keyboard state, mouse state, quadtree of clickables, list of
-- widgets.
data UIState = […]
uiCycle :: UIState -> ClientState -> IO ()
uiCycle uiState clientState = do
event <- waitEvent
let widget = findWidget uiState event
let (uiState', clientStateAction, ioAction) =
uiTransition widget event uiState clientState
-- Update game state (execute game logic).
let clientState' = clientStateAction clientState
-- Redraw appropriate surfaces etc.
ioAction
uiCycle uiState' clientState'
Widget is something like
data Widget =
Widget { wtType :: WidgetType
, wtParent :: Maybe Widget
--, wtRelated :: [Widget] -- Feels hacky.
, wtBox :: Rect
, wtPosition :: WidgetPosition
, wtAnchors :: [WidgetAnchor]
, wtEventHandlers :: Map Event EventHandler
, wtDisabled :: Bool
}
where
type EventHandler = (Event -> (UIState, ClientState) ->
(UIState, ClientState, IO ()))
When an event comes and appropriate widget is found, my program looks
for a handler for this kind of event in wtEventHandlers
for this widget,
and executes an action if such handler is found.
When a widget is created, its rect is added to a QuadTree
, which is
queried by findWidget
. The tree is updated when widgets are created
or removed.
However, it is very difficult to program in that way because UI states are already numerous (even for a simple UI with a few buttons and no menus).
I thought about implementing each widget as a state machine and composing these machines so that the number of states and transitions will be reduced. Perhaps it would be good to wrap it into a monad (like it is done in XMonad).
Things I have looked at:
- pygame dot org/tags/gui ← didn't get any usable ideas because everything there is very object-oriented;
- http://joyridelabs.de/game/code/ ← not applicable because they use Qt and I don't want such dependency;
- http://www.scribd.com/doc/19503176/The-Design-and-Implementation-of-xmonad
My questions are:
- Does describing widgets as state machines that "react" to certain events and "ignore" all others make sense to a widget toolkit designer? Is there a better (easier) way?
- Is organizing interface description into a
Monad
a good idea?
E. g.
mkGameScreenButtons = runGuiDef $ do
panicExitButton /// MouseDown ==> liftM exitGameNow
panicExitButton /// MouseIn ==> do hideGameScreen
muteSounds
muteMusic
panicExitButton /// MouseOut ==> do unhideGameScreen
unmuteSounds
unmuteMusic
gameSpeedSlider /// MouseUp ==> changeSpeed (wtValue gameSpeedSlider)
[...]
where panicExitButton = mkButton (5, 5) [AnchorTop, AnchorRight] "BOSS BUTTON"
[...]
- Is there any relevant literature I could read before moving further? Emphasis on functional approach would be nice.
- Is there a way to avoid writing a widget toolkit altogether? I want my game to be able to run in fullscreen mode, so Gtk2hs won't work.