minilight-0.4.1: src/Data/Component/Basic.hs
{-| The package provides the basics for all components in the library.
A component should have the followings (those can be omitted):
- position: @{x: int, y: int}@
- size: @{width: int, height: int}@
- color: @int[4]@
- font: @{family: string, bold: bool, italic: bool, size: int}@
-}
module Data.Component.Basic where
import Control.Lens hiding (contains)
import Control.Monad
import Data.Aeson
import Data.Aeson.Types
import qualified Data.HashMap.Strict as HM
import Data.Typeable
import qualified SDL
import qualified SDL.Vect as Vect
import MiniLight
-- | Basic config type
data Config = Config {
size :: Vect.V2 Int,
position :: Vect.V2 Int,
visible :: Bool
} deriving (Show)
makeClassy_ ''Config
defConfig :: Config
defConfig = Config {size = 0, position = 0, visible = True}
instance FromJSON Config where
parseJSON = withObject "config" $ \v -> do
sizeMaybe <- v .:? "size"
size <- (\w -> maybe (return 0) w sizeMaybe) $ withObject "size" $ \v ->
Vect.V2 <$> v .: "width" <*> v .: "height"
positionMaybe <- v .:? "position"
position <- (\w -> maybe (return 0) w positionMaybe) $ withObject "position" $ \v ->
Vect.V2 <$> v .: "x" <*> v .: "y"
visibleMaybe <- v .:? "visible"
let visible = maybe True id visibleMaybe
return $ Config size position visible
-- | This wrapper function is useful when you write your component config parser.
wrapConfig
:: (Config -> a -> Parser r) -> (Object -> Parser a) -> Value -> Parser r
wrapConfig f p = withObject "wrapConfig" $ \v -> do
other <- p v
conf <- parseJSON (Object v)
f conf other
-- | The rectangle region of the component.
areaRectangle :: Config -> SDL.Rectangle Int
areaRectangle conf = SDL.Rectangle (SDL.P (position conf)) (size conf)
-- | Basic signal type.
data Signal where
MousePressed
:: Vect.V2 Int -- ^ The relative position of the mouse pointer
-> Signal
MouseReleased
:: Vect.V2 Int -- ^ The relative position of the mouse pointer
-> Signal
MouseOver
:: Vect.V2 Int -- ^ The relative position of the mouse pointer
-> Signal
SetVisibility :: Bool -> Signal
deriving Typeable
instance EventType Signal where
getEventType (MousePressed _) = "mouse-pressed"
getEventType (MouseReleased _) = "mouse-released"
getEventType (MouseOver _) = "mouse-over"
getEventType (SetVisibility _) = "set-visibility"
getEventProperties (SetVisibility o) = HM.fromList [("visibility", Bool o)]
getEventProperties _ = error "not implemeneted yet"
-- | This automatically applies basic configuration such as: position.
wrapFigures :: Config -> [Figure] -> [Figure]
wrapFigures conf fs =
if not (conf ^. _visible) then [] else map (translate (position conf)) fs
-- | This wrapper function is useful when you write your own 'onSignal' component.
wrapSignal
:: ( HasLightEnv env
, HasLoopEnv env
, HasComponentEnv env
, MonadIO m
, ComponentUnit c
)
=> Lens' c Config -- ^ lens to 'Config'
-> (Event -> c -> LightT env m c) -- ^ custom @onSignal@ function
-> (Event -> c -> LightT env m c)
wrapSignal lens f ev comp = do
conf' <- handleBasicSignal ev (comp ^. lens)
when (comp ^. lens ^. _visible) $ emitBasicSignal ev conf'
f ev (comp & lens .~ conf')
-- | Basic signaling function. Signals are emitted towards the source component.
emitBasicSignal
:: (HasLightEnv env, HasLoopEnv env, HasComponentEnv env, MonadIO m)
=> Event
-> Config
-> LightT env m ()
emitBasicSignal (RawEvent (SDL.Event _ (SDL.MouseMotionEvent (SDL.MouseMotionEventData _ _ _ (SDL.P pos) _)))) conf
| contains (areaRectangle conf) (fmap fromEnum pos)
= view _uid
>>= \t -> emit (Just t) $ MouseOver $ fmap fromEnum pos - position conf
emitBasicSignal (RawEvent (SDL.Event _ (SDL.MouseButtonEvent (SDL.MouseButtonEventData _ state _ _ _ (SDL.P pos))))) conf
| contains (areaRectangle conf) (fmap fromEnum pos)
= view _uid >>= \t ->
emit (Just t)
$ ( case state of
SDL.Pressed -> MousePressed
SDL.Released -> MouseReleased
)
$ fmap fromEnum pos
- position conf
emitBasicSignal _ _ = return ()
-- | handle basic signals
handleBasicSignal
:: (HasLightEnv env, HasLoopEnv env, HasComponentEnv env, MonadIO m)
=> Event
-> Config
-> LightT env m Config
handleBasicSignal ev conf = case asSignal ev of
Just (SetVisibility b) -> return $ conf { visible = b }
_ -> return conf
contains :: (Ord a, Num a) => SDL.Rectangle a -> Vect.V2 a -> Bool
contains (SDL.Rectangle (Vect.P (Vect.V2 x y)) (Vect.V2 w h)) (Vect.V2 px py) =
x <= px && px <= x + w && y <= py && py <= y + h