packages feed

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

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

Slide 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.Slide (
  -- * Configuration
  SlideCfg,
  slideLeft,
  slideRight,
  slideTop,
  slideBottom,
  -- * Constructors
  animSlideIn,
  animSlideIn_,
  animSlideOut,
  animSlideOut_
) 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 SlideDirection
  = SlideLeft
  | SlideRight
  | SlideUp
  | SlideDown
  deriving (Eq, Show)

{-|
Configuration options for slide:

- '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 SlideCfg s e = SlideCfg {
  _slcDirection :: Maybe SlideDirection,
  _slcTransformCfg :: TransformCfg s e
} deriving (Eq, Show)

instance Default (SlideCfg s e) where
  def = SlideCfg {
    _slcDirection = Nothing,
    _slcTransformCfg = def
  }

instance Semigroup (SlideCfg s e) where
  (<>) fc1 fc2 = SlideCfg {
    _slcDirection = _slcDirection fc2 <|> _slcDirection fc1,
    _slcTransformCfg = _slcTransformCfg fc1 <> _slcTransformCfg fc2
  }

instance Monoid (SlideCfg s e) where
  mempty = def

instance CmbAutoStart (SlideCfg s e) where
  autoStart_ start = def {
    _slcTransformCfg = autoStart_ start
  }

instance CmbDuration (SlideCfg s e) Millisecond where
  duration dur = def {
    _slcTransformCfg = duration dur
  }

instance WidgetEvent e => CmbOnFinished (SlideCfg s e) e where
  onFinished handler = def {
    _slcTransformCfg = onFinished handler
  }

instance CmbOnFinishedReq (SlideCfg s e) s e where
  onFinishedReq req = def {
    _slcTransformCfg = onFinishedReq req
  }

-- | Slide from/to left.
slideLeft :: SlideCfg s e
slideLeft = def { _slcDirection = Just SlideLeft }

-- | Slide from/to right.
slideRight :: SlideCfg s e
slideRight = def { _slcDirection = Just SlideRight }

-- | Slide from/to top.
slideTop :: SlideCfg s e
slideTop = def { _slcDirection = Just SlideUp }

-- | Slide from/to bottom.
slideBottom :: SlideCfg s e
slideBottom = def { _slcDirection = Just SlideDown }

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

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

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

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

makeNode
  :: WidgetEvent e
  => [SlideCfg s e]
  -> WidgetNode s e
  -> Bool
  -> WidgetNode s e
makeNode configs managed isSlideIn = node where
  node = animTransform_ [_slcTransformCfg] f managed
  f t vp = [animTranslation $ Point (fx t vp) (fy t vp)]
  fx t (Rect _ _ w _) = case dir of
    SlideLeft -> -1*(step t)*w
    SlideRight -> (step t)*w
    _ -> 0
  fy t (Rect _ _ _ h) = case dir of
    SlideUp -> -1*(step t)*h
    SlideDown -> (step t)*h
    _ -> 0
  step t = if isSlideIn
    then 1-(fwdStep t)
    else fwdStep t
  fwdStep t = clamp 0 1 $ t/(fromIntegral dur)
  dir = fromMaybe SlideLeft _slcDirection
  dur = fromMaybe 500 _tfcDuration
  TransformCfg{..} = _slcTransformCfg
  SlideCfg{..} = mconcat configs