wxcore-0.10.1: samples/wx/TimeFlows.hs
{--------------------------------------------------------------------------------
Copyright (c) 2003 Daan Leijen.
"Time flows like a river" -- an old Fran demo :-)
Demonstrates the use of an idle event handler to implement a resource
aware gui that still does heavy animation.
--------------------------------------------------------------------------------}
module Main where
import System.CPUTime
import Graphics.UI.WXCore (getTextExtent)
import Graphics.UI.WX
{-------------------------------------------------------------------------
Types
-------------------------------------------------------------------------}
-- Time is in seconds, represented as a double.
type Time = Double
-- The (mouse) history consists of time/position pairs (and is never null)
type History = [(Time,Point)]
{-------------------------------------------------------------------------
Options
-------------------------------------------------------------------------}
-- The total time lag of the words behind the mouse cursor
timeSpan :: Time
timeSpan = 1
-- The flowing text
flowText = "Time flows like a river"
-- The font style
flowFont = fontSwiss{ _fontSize = 16 }
{-------------------------------------------------------------------------
The gui
-------------------------------------------------------------------------}
main
= start timeFlows
timeFlows
= do -- mouse history as list of time/position pairs: is never null!
vmouseHistory <- varCreate [(0,pt 0 0)]
-- create a frame.
f <- frame [ text := flowText]
p <- panel f [] -- draw in a panel
-- set event handlers
set p [ on paint := onPaint vmouseHistory
, on idle := onIdle vmouseHistory p
, on drag := onDrag vmouseHistory
]
-- set layout
set f [ layout := fill $ widget p
, clientSize := sz 300 300 -- initial size
]
return ()
{-------------------------------------------------------------------------
Event handlers
-------------------------------------------------------------------------}
-- repaint handler
onPaint vmouseHistory dc viewArea
= do history <- varGet vmouseHistory
time <- getTime
-- draw trace line
polyline dc (map snd history) [penColor := lightgrey]
-- draw the words
set dc [font := flowFont ]
mapM_ drawWord (wordPositions history timeSpan time flowText)
where
drawWord (pos,word)
= do -- center word
sz <- getTextExtent dc word
let newX = pointX pos - (sizeW sz `div` 2)
newY = pointY pos - (sizeH sz `div` 2)
-- and draw it.
drawText dc word (pt newX newY) []
-- idle event handler
onIdle :: Var History -> Window a -> IO Bool
onIdle vmouseHistory win
= do history <- varGet vmouseHistory
if (null (tail history))
then do -- don't call idle again until some other event happens
return False
else do time <- getTime
repaint win
-- prune the history
varSet vmouseHistory (prune time history)
return True
where
-- prune the history: only remember time/position pairs up to a certain time span.
prune time (h:hs)
= h:takeWhile (after (time-timeSpan)) hs
after time (t,p)
= time <= t
-- mouse drag handler
onDrag vmouseHistory mousePos
= do time <- getTime
-- prepend a new time/position pair
varUpdate vmouseHistory ((time,mousePos):)
return ()
{-------------------------------------------------------------------------
Helper functions
-------------------------------------------------------------------------}
-- Tuple each word in a string with its historic position, given a mouse
-- history, a time span, and current time.
wordPositions :: History -> Time -> Time -> String -> [(Point,String)]
wordPositions history timeSpan time
= wordPositionsAt history . wordTimes timeSpan time . words
-- Translate time/word pairs to position/word pairs given the mouse position history.
wordPositionsAt :: History -> [(Time,String)] -> [(Point,String)]
wordPositionsAt history timedWords
= [(posAtTime t history, word) | (t,word) <- timedWords]
-- | Return the mouse position at a certain time.
posAtTime :: Time -> History -> Point
posAtTime time [(t,pos)] = pos
posAtTime time ((t,pos):xs) | t <= time = pos
| otherwise = posAtTime time xs
-- | Evenly assign times to the words in a string, given a timeSpan and current time.
wordTimes :: Time -> Time -> [String] -> [(Time,String)]
wordTimes timeSpan time words
= let n = length words
delta = timeSpan / (fromIntegral n)
in zip (iterate (\t -> t-delta) time) words
-- Get the current Time
getTime :: IO Time
getTime
= do picoSecs <- getCPUTime
let time = (fromIntegral picoSecs) / 1.0e12
return time