content: {-|
  Module      : Monomer.Widgets.Singles.Spacer
  Copyright   : (c) 2018 Francisco Vallarino
  License     : BSD-3-Clause (see the LICENSE file)
  Maintainer  : fjvallarino@gmail.com
  Stability   : experimental
  Portability : non-portable
  
  Spacer is used for adding a fixed space between two widgets.
  
  @
  hstack [
    label \"Username\",
    spacer,
    label username
  ]
  @
  
  Filler is used for taking all the unused space between two widgets. Useful for
  alignment purposes.
  
  @
  hstack [
    label "Section title",
    filler,
    button \"Close\" CloseSection
  ]
  @
  
  Both adapt to the active layout direction.
  -}
  {-# LANGUAGE FlexibleContexts #-}
  
  module Monomer.Widgets.Singles.Spacer (
    --  * Configuration
    SpacerCfg,
    -- * Constructors
    spacer,
    spacer_,
    filler,
    filler_
  ) where
  
  import Control.Applicative ((<|>))
  import Control.Lens ((^.))
  import Data.Default
  import Data.Maybe
  
  import Monomer.Widgets.Single
  
  import qualified Monomer.Core.Lens as L
  
  {-|
  Configuration options for spacer widget:
  
  - 'width': the max width for spacer, the reference for filler.
  - 'resizeFactor': flexibility to have more or less space assigned.
  -}
  data SpacerCfg = SpacerCfg {
    _spcWidth :: Maybe Double,
    _spcFactor :: Maybe Double
  }
  
  instance Default SpacerCfg where
    def = SpacerCfg {
      _spcWidth = Nothing,
      _spcFactor = Nothing
    }
  
  instance Semigroup SpacerCfg where
    (<>) s1 s2 = SpacerCfg {
      _spcWidth = _spcWidth s2 <|> _spcWidth s1,
      _spcFactor = _spcFactor s2 <|> _spcFactor s1
    }
  
  instance Monoid SpacerCfg where
    mempty = def
  
  instance CmbWidth SpacerCfg where
    width w = def {
      _spcWidth = Just w
    }
  
  instance CmbResizeFactor SpacerCfg where
    resizeFactor f = def {
      _spcFactor = Just f
    }
  
  -- | Creates a spacer widget.
  spacer :: WidgetNode s e
  spacer = spacer_ def
  
  -- | Creates a spacer widget. Accepts config.
  spacer_ :: [SpacerCfg] -> WidgetNode s e
  spacer_ configs = defaultWidgetNode "spacer" widget where
    config = mconcat (resizeFactor 0 : configs)
    widget = makeSpacer config
  
  -- | Creates a filler widget.
  filler :: WidgetNode s e
  filler = filler_ def
  
  -- | Creates a filler widget. Accepts config.
  filler_ :: [SpacerCfg] -> WidgetNode s e
  filler_ configs = defaultWidgetNode "filler" widget where
    config = mconcat configs
    widget = makeSpacer config
  
  makeSpacer :: SpacerCfg -> Widget s e
  makeSpacer config = widget where
    widget = createSingle () def {
      singleGetSizeReq = getSizeReq
    }
  
    getSizeReq wenv node = sizeReq where
      direction = wenv ^. L.layoutDirection
      factor = fromMaybe 0.5 (_spcFactor config)
      isFixed = factor < 0.01
      width
        | isFixed = fromMaybe 10 (_spcWidth config)
        | otherwise = fromMaybe 5 (_spcWidth config)
  
      flexSide = flexSize 5 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)
  