{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE PatternGuards #-} module Graphics.Gloss.Internals.Interface.ViewPort.KeyMouse (callback_viewPort_keyMouse) where import Graphics.Gloss.Data.Vector import Graphics.Gloss.Geometry.Angle import Graphics.Gloss.Internals.Interface.ViewPort import Graphics.Gloss.Internals.Interface.ViewPort.Command import Graphics.Gloss.Internals.Interface.Backend import qualified Graphics.Gloss.Internals.Interface.ViewPort.ControlState as VPC import Control.Monad import Data.IORef import Data.Maybe -- | Callback to handle keyboard and mouse button events -- for controlling the viewport. callback_viewPort_keyMouse :: IORef ViewPort -- ^ ref to ViewPort state -> IORef VPC.State -- ^ ref to ViewPort Control state -> Callback callback_viewPort_keyMouse portRef controlRef = KeyMouse (viewPort_keyMouse portRef controlRef) viewPort_keyMouse :: IORef ViewPort -> IORef VPC.State -> KeyboardMouseCallback viewPort_keyMouse portRef controlRef stateRef key keyState keyMods pos = do commands <- controlRef `getsIORef` VPC.stateCommands {- putStr $ "keyMouse key = " ++ show key ++ "\n" ++ "keyMouse keyState = " ++ show keyState ++ "\n" ++ "keyMouse keyMods = " ++ show keyMods ++ "\n" -} -- Whether the user is holding down the translate button. currentlyTranslating <- liftM (isJust . VPC.stateTranslateMark) $ readIORef controlRef -- Whether the user is holding down the rotate button. currentlyRotating <- liftM (isJust . VPC.stateRotateMark) $ readIORef controlRef viewPort_keyMouse2 currentlyTranslating currentlyRotating commands where viewPort_keyMouse2 currentlyTranslating currentlyRotating commands -- restore viewport | isCommand commands CRestore key keyMods , keyState == Down = do portRef `modifyIORef` \s -> s { viewPortScale = 1 , viewPortTranslate = (0, 0) , viewPortRotate = 0 } postRedisplay stateRef -- zoom ---------------------------------------- -- zoom out | isCommand commands CBumpZoomOut key keyMods , keyState == Down = do controlZoomOut portRef controlRef postRedisplay stateRef -- zoom in | isCommand commands CBumpZoomIn key keyMods , keyState == Down = do controlZoomIn portRef controlRef postRedisplay stateRef -- bump ------------------------------------- -- bump left | isCommand commands CBumpLeft key keyMods , keyState == Down = do motionBump portRef (20, 0) postRedisplay stateRef -- bump right | isCommand commands CBumpRight key keyMods , keyState == Down = do motionBump portRef (-20, 0) postRedisplay stateRef -- bump up | isCommand commands CBumpUp key keyMods , keyState == Down = do motionBump portRef (0, 20) postRedisplay stateRef -- bump down | isCommand commands CBumpDown key keyMods , keyState == Down = do motionBump portRef (0, -20) postRedisplay stateRef -- bump clockwise | isCommand commands CBumpClockwise key keyMods , keyState == Down = do portRef `modifyIORef` \s -> s { viewPortRotate = (\r -> r + 5) $ viewPortRotate s } postRedisplay stateRef -- bump anti-clockwise | isCommand commands CBumpCClockwise key keyMods , keyState == Down = do portRef `modifyIORef` \s -> s { viewPortRotate = (\r -> r - 5) $ viewPortRotate s } postRedisplay stateRef -- translation -------------------------------------- -- start | isCommand commands CTranslate key keyMods , keyState == Down , not currentlyRotating = do let (posX, posY) = pos controlRef `modifyIORef` \s -> s { VPC.stateTranslateMark = Just ( posX , posY) } postRedisplay stateRef -- end -- We don't want to use 'isCommand' here because the user may have -- released the translation modifier key before the mouse button. -- and we still want to cancel the translation. | currentlyTranslating , keyState == Up = do controlRef `modifyIORef` \s -> s { VPC.stateTranslateMark = Nothing } postRedisplay stateRef -- rotation --------------------------------------- -- start | isCommand commands CRotate key keyMods , keyState == Down , not currentlyTranslating = do let (posX, posY) = pos controlRef `modifyIORef` \s -> s { VPC.stateRotateMark = Just ( posX , posY) } postRedisplay stateRef -- end -- We don't want to use 'isCommand' here because the user may have -- released the rotation modifier key before the mouse button, -- and we still want to cancel the rotation. | currentlyRotating , keyState == Up = do controlRef `modifyIORef` \s -> s { VPC.stateRotateMark = Nothing } postRedisplay stateRef -- carry on | otherwise = return () controlZoomIn :: IORef ViewPort -> IORef VPC.State -> IO () controlZoomIn portRef controlRef = do scaleStep <- controlRef `getsIORef` VPC.stateScaleStep portRef `modifyIORef` \s -> s { viewPortScale = viewPortScale s * scaleStep } controlZoomOut :: IORef ViewPort -> IORef VPC.State -> IO () controlZoomOut portRef controlRef = do scaleStep <- controlRef `getsIORef` VPC.stateScaleStep portRef `modifyIORef` \s -> s { viewPortScale = viewPortScale s / scaleStep } motionBump :: IORef ViewPort -> (Float, Float) -> IO () motionBump portRef (bumpX, bumpY) = do (transX, transY) <- portRef `getsIORef` viewPortTranslate scale <- portRef `getsIORef` viewPortScale r <- portRef `getsIORef` viewPortRotate let offset = (bumpX / scale, bumpY / scale) let (oX, oY) = rotateV (degToRad r) offset portRef `modifyIORef` \s -> s { viewPortTranslate = ( transX - oX , transY + oY) } getsIORef :: IORef a -> (a -> r) -> IO r getsIORef ref fun = liftM fun $ readIORef ref