packages feed

starrover2-0.0.9: src/TextScreen.hs

module TextScreen(loopTextScreen,
  makeTextScreen,
  drawButton,
  pressOneOfScreen,
  pressKeyScreen,
  pressAnyKeyScreen,
  menu)
where

import Data.List
import Control.Monad
import Control.Monad.State as State
import Prelude hiding (catch)

import Graphics.Rendering.OpenGL as OpenGL
import Graphics.UI.SDL as SDL
import Graphics.Rendering.FTGL as FTGL

import Camera
import Space
import SDLUtils
import Utils

writeLine :: (GLdouble, GLdouble) -> (Font, Color4 GLfloat, String) -> IO ()
writeLine (xstart, ystart) (f, c, s) = do
  loadIdentity
  translate (Vector3 xstart ystart (0 :: GLdouble))
  currentColor $= c
  forM_ (lines s) $ \str -> do
    renderFont f str FTGL.Front
    translate (Vector3 0 (-50) (0 :: GLdouble))

makeTextScreen :: (GLdouble, GLdouble) -> [(Font, Color4 GLfloat, String)] -> IO () -> IO ()
makeTextScreen (xstart, ystart) instructions additional = do
  clear [ColorBuffer,DepthBuffer]
  loadIdentity
  setCamera ((0, 0), (width, height))
  translate (Vector3 xstart ystart (0 :: GLdouble))
  forM_ instructions $ \(f, c, s) -> do
    currentColor $= c
    forM_ (lines s) $ \str -> do
      renderFont f str FTGL.Front
      translate (Vector3 0 (-50) (0 :: GLdouble))
  additional
  glSwapBuffers

-- TODO: write another version of this based on FPS
-- and another one with m Bool as event function
loopTextScreen :: (MonadIO m) => m () -> m (Maybe a) -> m a
loopTextScreen drawScreenFunc handleEventsFunc = untilDoneR $ do
  liftIO $ delay 10
  drawScreenFunc
  handleEventsFunc

drawButton :: Maybe (String, Font) -> ((GLdouble, GLdouble), (GLdouble, GLdouble)) -> IO ()
drawButton ms ((tlx, tly), (diffx, diffy)) = do
  loadIdentity
  translate $ Vector3 tlx (tly + 2) (0 :: GLdouble)
  currentColor $= Color4 1.0 1.0 1.0 1.0
  renderPrimitive LineLoop $
    mapM_ vertex [Vertex3 (0 :: GLdouble) 0 0, Vertex3 0 diffy 0, Vertex3 diffx diffy 0, Vertex3 diffx 0 0]
  case ms of
    Nothing -> return ()
    Just (str, f) -> do
      translate $ Vector3 10 8 (0 :: GLdouble)
      renderFont f str FTGL.Front

menu :: Int -> (Font, Color4 GLfloat, String) -> [(Font, Color4 GLfloat, String)] -> (Font, Color4 GLfloat, String) -> IO Int
menu defval (titlef, titlec, titlestr) options cursor = do
  let numitems = length options
  let title = (titlef, titlec, titlestr ++ replicate (4 - length (lines titlestr)) '\n')
  let drawCursor n = liftIO $ writeLine (130, 400 - 50 * fromIntegral n) cursor
  let buttoncoords :: (Num a) => [((a, a), (a, a))]
      buttoncoords = [((180, 385 - 50 * fromIntegral i), (300, 40)) | i <- [1..numitems]]
  let drawButtons = forM_ buttoncoords $ \b -> do
      drawButton Nothing b
  if numitems == 0
    then return 0
    else do
      n <- Prelude.flip evalStateT (clamp 1 numitems defval) $ do
        let drawfunc = do n <- State.get 
                          liftIO $ makeTextScreen (200, 500)
                            (title:options)
                            (drawButtons >> drawCursor n)
        let getInput = do
              evts <- liftIO $ pollAllSDLEvents
              when (keyWasPressed SDLK_DOWN evts) $
                modify (\p -> min numitems $ p + 1)
              when (keyWasPressed SDLK_UP evts) $
                modify (\p -> max 1 $ p - 1)
              if oneofKeyWasPressed [SDLK_RETURN, SDLK_SPACE] evts
                then State.get >>= return . Just 
                else do
                  let mbutton = mouseClickInAny height [ButtonLeft] buttoncoords evts
                  return $ mbutton >>= (fmap . fmap) (+1) (Prelude.flip elemIndex buttoncoords)
        loopTextScreen drawfunc getInput
      return n

pressOneOfScreen :: (MonadIO m) => m () -> [SDLKey] -> m SDLKey
pressOneOfScreen scr keys = 
  loopTextScreen scr
     (liftIO $ pollAllSDLEvents >>= return . specificKeyPressed keys)

pressKeyScreen :: (MonadIO m) => m () -> SDLKey -> m ()
pressKeyScreen scr k =
  loopTextScreen scr
     (liftIO $ pollAllSDLEvents >>= return . boolToMaybe . keyWasPressed k)

pressAnyKeyScreen :: (MonadIO m) => m () -> m ()
pressAnyKeyScreen scr =
  loopTextScreen scr
     (liftIO $ pollAllSDLEvents >>= return . boolToMaybe . anyKeyOrMouseWasPressed)