packages feed

monomer-1.0.0.3: src/Monomer/Widgets/Singles/SeparatorLine.hs

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

SeparatorLine is used for adding a separator line between two widgets. It adapts
to the active layout direction, creating a vertical line on a horizontal layout
and viceversa.

The line has the provided width in the direction orthogonal to the layout
direction, and takes all the available space in the other direction. In case of
wanting a shorter line, padding should be used.
-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE StrictData #-}

module Monomer.Widgets.Singles.SeparatorLine (
  -- * Configuration
  SeparatorLineCfg,
  -- * Constructors
  separatorLine,
  separatorLine_
) where

import Control.Applicative ((<|>))
import Control.Lens ((^.))
import Data.Default
import Data.Maybe
import Data.Tuple

import Monomer.Widgets.Single

import qualified Monomer.Core.Lens as L

{-|
Configuration options for separatorLine:

- 'width': the max width of the line.
- 'resizeFactor': flexibility to have more or less spaced assigned.
-}
data SeparatorLineCfg = SeparatorLineCfg {
  _slcWidth :: Maybe Double,
  _slcFactor :: Maybe Double
}

instance Default SeparatorLineCfg where
  def = SeparatorLineCfg {
    _slcWidth = Nothing,
    _slcFactor = Nothing
  }

instance Semigroup SeparatorLineCfg where
  (<>) s1 s2 = SeparatorLineCfg {
    _slcWidth = _slcWidth s2 <|> _slcWidth s1,
    _slcFactor = _slcFactor s2 <|> _slcFactor s1
  }

instance Monoid SeparatorLineCfg where
  mempty = def

instance CmbWidth SeparatorLineCfg where
  width w = def {
    _slcWidth = Just w
  }

instance CmbResizeFactor SeparatorLineCfg where
  resizeFactor f = def {
    _slcFactor = Just f
  }

-- | Creates a separatorLine widget.
separatorLine :: WidgetNode s e
separatorLine = separatorLine_ def

-- | Creates a separatorLine widget. Accepts config.
separatorLine_ :: [SeparatorLineCfg] -> WidgetNode s e
separatorLine_ configs = defaultWidgetNode "separatorLine" widget where
  config = mconcat (resizeFactor 0 : configs)
  widget = makeSeparatorLine config

makeSeparatorLine :: SeparatorLineCfg -> Widget s e
makeSeparatorLine !config = widget where
  widget = createSingle () def {
    singleGetBaseStyle = getBaseStyle,
    singleGetSizeReq = getSizeReq,
    singleRender = render
  }

  getBaseStyle wenv node = Just style where
    style = collectTheme wenv L.separatorLineStyle

  getSizeReq wenv node = sizeReq where
    theme = currentTheme wenv node
    direction = wenv ^. L.layoutDirection
    width = fromMaybe (theme ^. L.separatorLineWidth) (_slcWidth config)
    factor = fromMaybe 0 (_slcFactor config)

    isFixed = factor < 0.01
    flexSide = flexSize 10 0.5
    fixedW = fixedSize width
    flexW = flexSize width factor
    expandW = expandSize width factor

    sizeReq
      | isFixed && direction == LayoutNone = (fixedW, fixedW)
      | isFixed && direction == LayoutHorizontal = (fixedW, flexSide)
      | isFixed = (flexSide, fixedW)
      | direction == LayoutNone = (expandW, expandW)
      | direction == LayoutHorizontal = (expandW, flexW)
      | otherwise = (flexW, expandW)

  render wenv node renderer = do
    beginPath renderer
    setFillColor renderer fgColor
    renderRect renderer lineRect
    fill renderer
    where
      theme = currentTheme wenv node
      style = currentStyle wenv node
      direction = wenv ^. L.layoutDirection
      fgColor = styleFgColor style
      width = fromMaybe (theme ^. L.separatorLineWidth) (_slcWidth config)

      Rect cx cy cw ch = getContentArea node style
      lineW = cx + (cw - width) / 2
      lineH = cy + (ch - width) / 2
      lineRect
        | direction == LayoutNone = Rect cx cy cw ch
        | direction == LayoutHorizontal = Rect lineW cy width ch
        | otherwise = Rect cx lineH cw width