packages feed

monomer-1.6.0.0: src/Monomer/Widgets/Animation/Wipe.hs

{-|
Module      : Monomer.Widgets.Animation.Wipe
Copyright   : (c) 2023 Ruslan Gadeev, Francisco Vallarino
License     : BSD-3-Clause (see the LICENSE file)
Maintainer  : fjvallarino@gmail.com
Stability   : experimental
Portability : non-portable

Wipe animation widget. Wraps a child widget whose content will be animated.

Messages:

- Accepts a 'AnimationMsg', used to control the state of the animation.
-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE Strict #-}

module Monomer.Widgets.Animation.Wipe (
  -- * Configuration
  WipeCfg,
  wipeLeft,
  wipeRight,
  wipeTop,
  wipeBottom,
  wipeDoorH,
  wipeDoorV,
  wipeRect,
  -- * Constructors
  animWipeIn,
  animWipeIn_,
  animWipeOut,
  animWipeOut_
) where

import Control.Applicative ((<|>))
import Control.Lens ((&), (.~))
import Data.Default
import Data.Maybe

import Monomer.Helper
import Monomer.Widgets.Container
import Monomer.Widgets.Animation.Transform

import qualified Monomer.Lens as L

data WipeDirection
  = WipeLeft
  | WipeRight
  | WipeTop
  | WipeBottom
  | WipeDoorH
  | WipeDoorV
  | WipeRect
  deriving (Eq, Show)

{-|
Configuration options for wipe:

- 'autoStart': whether the first time the widget is added, animation should run.
- 'duration': how long the animation lasts in ms.
- 'onFinished': event to raise when animation is complete.
- 'onFinishedReq': 'WidgetRequest' to generate when animation is complete.
- Individual combinators for direction.
-}
data WipeCfg s e = WipeCfg {
  _wpcDirection :: Maybe WipeDirection,
  _wpcTransformCfg :: TransformCfg s e
} deriving (Eq, Show)

instance Default (WipeCfg s e) where
  def = WipeCfg {
    _wpcDirection = Nothing,
    _wpcTransformCfg = def
  }

instance Semigroup (WipeCfg s e) where
  (<>) wc1 wc2 = WipeCfg {
    _wpcDirection = _wpcDirection wc2 <|> _wpcDirection wc1,
    _wpcTransformCfg = _wpcTransformCfg wc1 <> _wpcTransformCfg wc2
  }

instance Monoid (WipeCfg s e) where
  mempty = def

instance CmbAutoStart (WipeCfg s e) where
  autoStart_ start = def {
    _wpcTransformCfg = autoStart_ start
  }

instance CmbDuration (WipeCfg s e) Millisecond where
  duration dur = def {
    _wpcTransformCfg = duration dur
  }

instance WidgetEvent e => CmbOnFinished (WipeCfg s e) e where
  onFinished handler = def {
    _wpcTransformCfg = onFinished handler
  }

instance CmbOnFinishedReq (WipeCfg s e) s e where
  onFinishedReq req = def {
    _wpcTransformCfg = onFinishedReq req
  }

-- | Wipe from/to left.
wipeLeft :: WipeCfg s e
wipeLeft = def { _wpcDirection = Just WipeLeft }

-- | Wipe from/to right.
wipeRight :: WipeCfg s e
wipeRight = def { _wpcDirection = Just WipeRight }

-- | Wipe from/to top.
wipeTop :: WipeCfg s e
wipeTop = def { _wpcDirection = Just WipeTop }

-- | Wipe from/to bottom.
wipeBottom :: WipeCfg s e
wipeBottom = def { _wpcDirection = Just WipeBottom }

-- | Wipe horizontally in a door shape.
wipeDoorH :: WipeCfg s e
wipeDoorH = def { _wpcDirection = Just WipeDoorH }

-- | Wipe vertically in a door shape.
wipeDoorV :: WipeCfg s e
wipeDoorV = def { _wpcDirection = Just WipeDoorV }

-- | Wipe in a rectangle shape.
wipeRect :: WipeCfg s e
wipeRect = def { _wpcDirection = Just WipeRect }

-- | Animates a widget from the left to fully visible.
animWipeIn
  :: WidgetEvent e
  => WidgetNode s e  -- ^ The child node.
  -> WidgetNode s e  -- ^ The created animation container.
animWipeIn managed = animWipeIn_ def managed

-- | Animates a widget from the provided direction to fully visible (defaults
--   to left). Accepts config.
animWipeIn_
  :: WidgetEvent e
  => [WipeCfg s e]     -- ^ The config options.
  -> WidgetNode s e  -- ^ The child node.
  -> WidgetNode s e  -- ^ The created animation container.
animWipeIn_ configs managed = makeNode configs managed True
  & L.info . L.widgetType .~ "animWipeIn"

-- | Animates a widget to the left from visible to not visible.
animWipeOut
  :: WidgetEvent e
  => WidgetNode s e  -- ^ The child node.
  -> WidgetNode s e  -- ^ The created animation container.
animWipeOut managed = animWipeOut_ def managed

-- | Animates a widget to the provided direction from visible to not
--   visible (defaults to left). Accepts config.
animWipeOut_
  :: WidgetEvent e
  => [WipeCfg s e]     -- ^ The config options.
  -> WidgetNode s e  -- ^ The child node.
  -> WidgetNode s e  -- ^ The created animation container.
animWipeOut_ configs managed = makeNode configs managed False
  & L.info . L.widgetType .~ "animWipeOut"

makeNode
  :: WidgetEvent e
  => [WipeCfg s e]
  -> WidgetNode s e
  -> Bool
  -> WidgetNode s e
makeNode configs managed isWipeIn = node where
  node = animTransform_ [_wpcTransformCfg] f managed
  f t (Rect x y w h) = [animScissor vp] where
    vp = case dir of
      WipeLeft -> Rect x y dw h
      WipeRight -> Rect (x+(1-(step t))*w) y dw h
      WipeTop -> Rect x y w dh
      WipeBottom -> Rect x (y+(1-(step t))*h) w dh
      WipeDoorH -> Rect dx y dw h
      WipeDoorV -> Rect x dy w dh
      WipeRect -> Rect dx dy dw dh
    (dx, dy) = (x+(1-(step t))*w/2, y+(1-(step t))*h/2)
    (dw, dh) = ((step t)*w, (step t)*h)
  step t = if isWipeIn
    then fwdStep t
    else 1-(fwdStep t)
  fwdStep t = clamp 0 1 $ t/(fromIntegral dur)
  dir = fromMaybe WipeLeft _wpcDirection
  dur = fromMaybe 500 _tfcDuration
  TransformCfg{..} = _wpcTransformCfg
  WipeCfg{..} = mconcat configs