    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, PatternGuards #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.Spacing
    -- Description :  Add a configurable amount of space around windows.
    -- Copyright   :  (C) --   Brent Yorgey
    --                    2018 Yclept Nemo
    -- License     :  BSD-style (see LICENSE)
    --
    -- Maintainer  :  <byorgey@gmail.com>
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- Add a configurable amount of space around windows.
    --
    -- Note: For space\/gaps along edges of the /screen/ see "XMonad.Layout.Gaps".
    -----------------------------------------------------------------------------

    module XMonad.Layout.Spacing
        ( -- * Usage
          -- $usage
          Spacing (..)
        , spacingRaw
        , spacing, spacingWithEdge
        , smartSpacing, smartSpacingWithEdge

          -- * Modify Spacing
        , SpacingModifier (..)
        , setSmartSpacing
        , setScreenSpacing, setScreenSpacingEnabled
        , setWindowSpacing, setWindowSpacingEnabled
        , toggleSmartSpacing
        , toggleScreenSpacingEnabled
        , toggleWindowSpacingEnabled
        , setScreenWindowSpacing
        , incWindowSpacing, incScreenSpacing
        , decWindowSpacing, decScreenSpacing
        , incScreenWindowSpacing, decScreenWindowSpacing

          -- * Modify Borders
        , Border (..)
        , borderMap, borderIncrementBy
        ) where

    import           XMonad
    import           XMonad.StackSet                    as W
    import qualified XMonad.Util.Rectangle              as R
    import           XMonad.Layout.LayoutModifier
    import           XMonad.Actions.MessageFeedback

    -- $usage
    -- You can use this module by importing it into your @xmonad.hs@
    -- file:
    --
    -- > import XMonad.Layout.Spacing
    --
    -- and, for example, modifying your @layoutHook@ as follows:
    --
    -- > main :: IO ()
    -- > main = xmonad $ def
    -- >   { layoutHook = spacingWithEdge 10 $ myLayoutHook
    -- >   }
    -- >
    -- > myLayoutHook = Full ||| ...
    --
    -- The above would add a 10 pixel gap around windows on all sides, as
    -- well as add the same amount of spacing around the edges of the
    -- screen.  If you only want to add spacing around windows, you can use
    -- 'spacing' instead.
    --
    -- There is also the 'spacingRaw' command, for more fine-grained
    -- control.  For example:
    --
    -- > layoutHook = spacingRaw True (Border 0 10 10 10) True (Border 10 10 10 10) True
    -- >            $ myLayoutHook
    --
    -- Breaking this down, the above would do the following:
    --
    --   - @True@: Enable the 'smartBorder' to not apply borders when there
    --     is only one window.
    --
    --   - @(Border 0 10 10 10)@: Add a 'screenBorder' of 10 pixels in every
    --     direction but the top.
    --
    --   - @True@: Enable the 'screenBorder'.
    --
    --   - @(Border 10 10 10 10)@: Add a 'windowBorder' of 10 pixels in
    --     every direction.
    --
    --   - @True@: Enable the 'windowBorder'.
    --
    -- __Warning__: If you also use the 'avoidStruts' layout modifier, it
    -- must come /before/ any of these modifiers. See the documentation of
    -- 'avoidStruts' for details.

    -- | Represent the borders of a rectangle.
    data Border = Border
        { Border -> Integer
    top       :: Integer
        , Border -> Integer
    bottom    :: Integer
        , Border -> Integer
    right     :: Integer
        , Border -> Integer
    left      :: Integer
        } deriving (Int -> Border -> ShowS
    [Border] -> ShowS
    Border -> String
    (Int -> Border -> ShowS)
    -> (Border -> String) -> ([Border] -> ShowS) -> Show Border
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Border -> ShowS
    showsPrec :: Int -> Border -> ShowS
    $cshow :: Border -> String
    show :: Border -> String
    $cshowList :: [Border] -> ShowS
    showList :: [Border] -> ShowS
    Show,ReadPrec [Border]
    ReadPrec Border
    Int -> ReadS Border
    ReadS [Border]
    (Int -> ReadS Border)
    -> ReadS [Border]
    -> ReadPrec Border
    -> ReadPrec [Border]
    -> Read Border
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS Border
    readsPrec :: Int -> ReadS Border
    $creadList :: ReadS [Border]
    readList :: ReadS [Border]
    $creadPrec :: ReadPrec Border
    readPrec :: ReadPrec Border
    $creadListPrec :: ReadPrec [Border]
    readListPrec :: ReadPrec [Border]
    Read)

    -- | A 'LayoutModifier' providing customizable screen and window borders.
    -- Borders are clamped to @[0,Infinity]@ before being applied.
    data Spacing a = Spacing
        { forall a. Spacing a -> Bool
    smartBorder           :: Bool
            -- ^ When @True@ borders are not applied if
            --   there fewer than two windows.
        , forall a. Spacing a -> Border
    screenBorder          :: Border
            -- ^ The screen border.
        , forall a. Spacing a -> Bool
    screenBorderEnabled   :: Bool
            -- ^ Is the screen border enabled?
        , forall a. Spacing a -> Border
    windowBorder          :: Border
            -- ^ The window borders.
        , forall a. Spacing a -> Bool
    windowBorderEnabled   :: Bool
            -- ^ Is the window border enabled?
        } deriving (Int -> Spacing a -> ShowS
    [Spacing a] -> ShowS
    Spacing a -> String
    (Int -> Spacing a -> ShowS)
    -> (Spacing a -> String)
    -> ([Spacing a] -> ShowS)
    -> Show (Spacing a)
    forall a. Int -> Spacing a -> ShowS
    forall a. [Spacing a] -> ShowS
    forall a. Spacing a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall a. Int -> Spacing a -> ShowS
    showsPrec :: Int -> Spacing a -> ShowS
    $cshow :: forall a. Spacing a -> String
    show :: Spacing a -> String
    $cshowList :: forall a. [Spacing a] -> ShowS
    showList :: [Spacing a] -> ShowS
    Show,ReadPrec [Spacing a]
    ReadPrec (Spacing a)
    Int -> ReadS (Spacing a)
    ReadS [Spacing a]
    (Int -> ReadS (Spacing a))
    -> ReadS [Spacing a]
    -> ReadPrec (Spacing a)
    -> ReadPrec [Spacing a]
    -> Read (Spacing a)
    forall a. ReadPrec [Spacing a]
    forall a. ReadPrec (Spacing a)
    forall a. Int -> ReadS (Spacing a)
    forall a. ReadS [Spacing a]
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: forall a. Int -> ReadS (Spacing a)
    readsPrec :: Int -> ReadS (Spacing a)
    $creadList :: forall a. ReadS [Spacing a]
    readList :: ReadS [Spacing a]
    $creadPrec :: forall a. ReadPrec (Spacing a)
    readPrec :: ReadPrec (Spacing a)
    $creadListPrec :: forall a. ReadPrec [Spacing a]
    readListPrec :: ReadPrec [Spacing a]
    Read)

    instance Eq a => LayoutModifier Spacing a where
        -- This is a bit of a chicken-and-egg problem - the visible window list has
        -- yet to be generated. Several workarounds to incorporate the screen
        -- border:
        -- 1. Call 'runLayout' twice, with/without the screen border. Since layouts
        --    run arbitrary X actions, this breaks an important underlying
        --    assumption. Also, doesn't really solve the chicken-egg problem.
        -- 2. Create the screen border after and if the child layout returns more
        --    than one window. Unfortunately this breaks the window ratios
        --    presented by the child layout, another important assumption.
        -- 3. Create the screen border before, and remove it after and if the child
        --    layout returns fewer than two visible windows. This is somewhat hacky
        --    but probably the best option. Could significantly modify the child
        --    layout if it would have returned more than one window given the space
        --    of the screen border, but this is the underlying chicken-egg problem,
        --    and some concession must be made:
        --      * no border -> multiple windows
        --      * border -> single window
        --    Also slightly breaks layouts that expect to present absolutely-sized
        --    windows; a single window will be scaled up by the border size.
        --    Overall these are trivial assumptions.
        --
        -- Note #1: the original code counted the windows of the 'Workspace' stack,
        -- and so generated incorrect results even for the builtin 'Full' layout.
        -- Even though most likely true, it isn't guaranteed that a layout will
        -- never return windows not in the stack, specifically that an empty stack
        -- will lead to 0 visible windows and a stack with a single window will
        -- lead to 0-1 visible windows (see 'XMonad.Layout.Decoration'). So as much
        -- as I would like to pass a rectangle without screen borders to the child
        -- layout when appropriate (per the original approach), I can't. Since the
        -- screen border is always present whether displayed or not, child layouts
        -- can't depend on an accurate layout rectangle.
        --
        -- Note #2: If there are fewer than two stack windows displayed, the stack
        -- window (if present) is scaled up while the non-stack windows are moved a
        -- border-dependent amount based on their quadrant. So a non-stack window
        -- in the top-left quadrant will be moved using only the border's top and
        -- left components. Originally I was going to use an edge-attachment
        -- algorithm, but this is much simpler and covers most cases. Edge
        -- attachment would have scaled non-stack windows, but most non-stack
        -- windows are created by XMonad and therefore cannot be scaled. I suggest
        -- this layout be disabled for any incompatible child layouts.
        modifyLayout :: forall (l :: * -> *).
    LayoutClass l a =>
    Spacing a
    -> Workspace String (l a) a
    -> Rectangle
    -> X ([(a, Rectangle)], Maybe (l a))
    modifyLayout (Spacing Bool
    _b Border
    _sb Bool
    False Border
    _wb Bool
    _wbe) Workspace String (l a) a
    wsp Rectangle
    lr =
            Workspace String (l a) a
    -> Rectangle -> X ([(a, Rectangle)], Maybe (l a))
    forall (layout :: * -> *) a.
    LayoutClass layout a =>
    Workspace String (layout a) a
    -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
    runLayout Workspace String (l a) a
    wsp Rectangle
    lr
        modifyLayout (Spacing Bool
    b Border
    sb Bool
    _sbe Border
    _wb Bool
    _wbe) Workspace String (l a) a
    wsp Rectangle
    lr = do
            let sb1 :: Border
    sb1 = Border -> Border
    borderClampGTZero Border
    sb
                lr' :: Rectangle
    lr' = Border -> Integer -> Rectangle -> Rectangle
    withBorder' Border
    sb1 Integer
    2 Rectangle
    lr
                sb2 :: Border
    sb2 = Rectangle -> Rectangle -> Border
    toBorder Rectangle
    lr' Rectangle
    lr
            ([(a, Rectangle)]
    wrs,Maybe (l a)
    ml) <- Workspace String (l a) a
    -> Rectangle -> X ([(a, Rectangle)], Maybe (l a))
    forall (layout :: * -> *) a.
    LayoutClass layout a =>
    Workspace String (layout a) a
    -> Rectangle -> X ([(a, Rectangle)], Maybe (layout a))
    runLayout Workspace String (l a) a
    wsp Rectangle
    lr'
            let ff :: (a, Rectangle) -> (a, [(a, Rectangle)]) -> (a, [(a, Rectangle)])
    ff (a
    w,Rectangle
    wr) (a
    i,[(a, Rectangle)]
    ps) = if a
    w a -> [a] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (Maybe (Stack a) -> [a]
    forall a. Maybe (Stack a) -> [a]
    W.integrate' (Maybe (Stack a) -> [a])
    -> (Workspace String (l a) a -> Maybe (Stack a))
    -> Workspace String (l a) a
    -> [a]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Workspace String (l a) a -> Maybe (Stack a)
    forall i l a. Workspace i l a -> Maybe (Stack a)
    W.stack (Workspace String (l a) a -> [a])
    -> Workspace String (l a) a -> [a]
    forall a b. (a -> b) -> a -> b
    $ Workspace String (l a) a
    wsp)
                                   then let wr' :: Rectangle
    wr' = Border -> Integer -> Rectangle -> Rectangle
    withBorder' Border
    sb2 Integer
    2 Rectangle
    wr
                                        in  (a
    ia -> a -> a
    forall a. Num a => a -> a -> a
    +a
    1,(a
    w,Rectangle
    wr')(a, Rectangle) -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. a -> [a] -> [a]
    :[(a, Rectangle)]
    ps)
                                   else let wr' :: Rectangle
    wr' = Rectangle -> Rectangle -> Border -> Rectangle
    moveByQuadrant Rectangle
    lr Rectangle
    wr Border
    sb2
                                        in  (a
    i,(a
    w,Rectangle
    wr')(a, Rectangle) -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. a -> [a] -> [a]
    :[(a, Rectangle)]
    ps)
                (Integer
    c,[(a, Rectangle)]
    wrs') = ((a, Rectangle)
     -> (Integer, [(a, Rectangle)]) -> (Integer, [(a, Rectangle)]))
    -> (Integer, [(a, Rectangle)])
    -> [(a, Rectangle)]
    -> (Integer, [(a, Rectangle)])
    forall a b. (a -> b -> b) -> b -> [a] -> b
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr (a, Rectangle)
    -> (Integer, [(a, Rectangle)]) -> (Integer, [(a, Rectangle)])
    forall {a}.
    Num a =>
    (a, Rectangle) -> (a, [(a, Rectangle)]) -> (a, [(a, Rectangle)])
    ff (Integer
    0::Integer,[]) [(a, Rectangle)]
    wrs
            ([(a, Rectangle)], Maybe (l a))
    -> X ([(a, Rectangle)], Maybe (l a))
    forall a. a -> X a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (([(a, Rectangle)], Maybe (l a))
     -> X ([(a, Rectangle)], Maybe (l a)))
    -> ([(a, Rectangle)], Maybe (l a))
    -> X ([(a, Rectangle)], Maybe (l a))
    forall a b. (a -> b) -> a -> b
    $ if Integer
    c Integer -> Integer -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Integer
    1 Bool -> Bool -> Bool
    && Bool
    b
                then ([(a, Rectangle)]
    wrs',Maybe (l a)
    ml)
                else ([(a, Rectangle)]
    wrs,Maybe (l a)
    ml)
          where
            moveByQuadrant :: Rectangle -> Rectangle -> Border -> Rectangle
            moveByQuadrant :: Rectangle -> Rectangle -> Border -> Rectangle
    moveByQuadrant Rectangle
    rr mr :: Rectangle
    mr@Rectangle{rect_x :: Rectangle -> Position
    rect_x = Position
    x, rect_y :: Rectangle -> Position
    rect_y = Position
    y} (Border Integer
    bt Integer
    bb Integer
    br Integer
    bl) =
                let (Ratio Integer
    rcx,Ratio Integer
    rcy) = Rectangle -> (Ratio Integer, Ratio Integer)
    R.center Rectangle
    rr
                    (Ratio Integer
    mcx,Ratio Integer
    mcy) = Rectangle -> (Ratio Integer, Ratio Integer)
    R.center Rectangle
    mr
                    dx :: Integer
    dx = Ordering -> (Integer, Integer, Integer) -> Integer
    forall a. Ordering -> (a, a, a) -> a
    orderSelect (Ratio Integer -> Ratio Integer -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare Ratio Integer
    mcx Ratio Integer
    rcx) (Integer
    bl,Integer
    0,Integer -> Integer
    forall a. Num a => a -> a
    negate Integer
    br)
                    dy :: Integer
    dy = Ordering -> (Integer, Integer, Integer) -> Integer
    forall a. Ordering -> (a, a, a) -> a
    orderSelect (Ratio Integer -> Ratio Integer -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare Ratio Integer
    mcy Ratio Integer
    rcy) (Integer
    bt,Integer
    0,Integer -> Integer
    forall a. Num a => a -> a
    negate Integer
    bb)
                in  Rectangle
    mr { rect_x = x + fromIntegral dx, rect_y = y + fromIntegral dy }

        -- This is run after 'modifyLayout' but receives the original stack, not
        -- one possibly modified by the child layout. Does not remove borders from
        -- windows not in the stack, i.e. decorations generated by
        -- 'XMonad.Layout.Decorations'.
        pureModifier :: Spacing a
    -> Rectangle
    -> Maybe (Stack a)
    -> [(a, Rectangle)]
    -> ([(a, Rectangle)], Maybe (Spacing a))
    pureModifier (Spacing Bool
    _b Border
    _sb Bool
    _sbe Border
    _wb Bool
    False) Rectangle
    _lr Maybe (Stack a)
    _mst [(a, Rectangle)]
    wrs =
            ([(a, Rectangle)]
    wrs, Maybe (Spacing a)
    forall a. Maybe a
    Nothing)
        pureModifier (Spacing Bool
    b Border
    _sb Bool
    _sbe Border
    wb Bool
    _wbe) Rectangle
    _lr Maybe (Stack a)
    mst [(a, Rectangle)]
    wrs =
            let wb' :: Border
    wb' = Border -> Border
    borderClampGTZero Border
    wb
                ff :: (a, Rectangle) -> (a, [(a, Rectangle)]) -> (a, [(a, Rectangle)])
    ff p :: (a, Rectangle)
    p@(a
    w,Rectangle
    wr) (a
    i,[(a, Rectangle)]
    ps) = if a
    w a -> [a] -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` Maybe (Stack a) -> [a]
    forall a. Maybe (Stack a) -> [a]
    W.integrate' Maybe (Stack a)
    mst
                                     then let wr' :: Rectangle
    wr' = Border -> Integer -> Rectangle -> Rectangle
    withBorder' Border
    wb' Integer
    2 Rectangle
    wr
                                          in  (a
    ia -> a -> a
    forall a. Num a => a -> a -> a
    +a
    1,(a
    w,Rectangle
    wr')(a, Rectangle) -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. a -> [a] -> [a]
    :[(a, Rectangle)]
    ps)
                                     else (a
    i,(a, Rectangle)
    p(a, Rectangle) -> [(a, Rectangle)] -> [(a, Rectangle)]
    forall a. a -> [a] -> [a]
    :[(a, Rectangle)]
    ps)
                (Integer
    c,[(a, Rectangle)]
    wrs') = ((a, Rectangle)
     -> (Integer, [(a, Rectangle)]) -> (Integer, [(a, Rectangle)]))
    -> (Integer, [(a, Rectangle)])
    -> [(a, Rectangle)]
    -> (Integer, [(a, Rectangle)])
    forall a b. (a -> b -> b) -> b -> [a] -> b
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr (a, Rectangle)
    -> (Integer, [(a, Rectangle)]) -> (Integer, [(a, Rectangle)])
    forall {a}.
    Num a =>
    (a, Rectangle) -> (a, [(a, Rectangle)]) -> (a, [(a, Rectangle)])
    ff (Integer
    0::Integer,[]) [(a, Rectangle)]
    wrs
            in  if Integer
    c Integer -> Integer -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Integer
    1 Bool -> Bool -> Bool
    && Bool
    b
                then ([(a, Rectangle)]
    wrs, Maybe (Spacing a)
    forall a. Maybe a
    Nothing)
                else ([(a, Rectangle)]
    wrs', Maybe (Spacing a)
    forall a. Maybe a
    Nothing)

        pureMess :: Spacing a -> SomeMessage -> Maybe (Spacing a)
    pureMess s :: Spacing a
    s@(Spacing Bool
    b Border
    sb Bool
    sbe Border
    wb Bool
    wbe) SomeMessage
    m
            | Just (ModifySmartBorder Bool -> Bool
    f) <- SomeMessage -> Maybe SpacingModifier
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m
            = Spacing a -> Maybe (Spacing a)
    forall a. a -> Maybe a
    Just (Spacing a -> Maybe (Spacing a)) -> Spacing a -> Maybe (Spacing a)
    forall a b. (a -> b) -> a -> b
    $ Spacing a
    s { smartBorder = f b }
            | Just (ModifyScreenBorder Border -> Border
    f) <- SomeMessage -> Maybe SpacingModifier
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m
            = Spacing a -> Maybe (Spacing a)
    forall a. a -> Maybe a
    Just (Spacing a -> Maybe (Spacing a)) -> Spacing a -> Maybe (Spacing a)
    forall a b. (a -> b) -> a -> b
    $ Spacing a
    s { screenBorder = f sb }
            | Just (ModifyScreenBorderEnabled Bool -> Bool
    f) <- SomeMessage -> Maybe SpacingModifier
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m
            = Spacing a -> Maybe (Spacing a)
    forall a. a -> Maybe a
    Just (Spacing a -> Maybe (Spacing a)) -> Spacing a -> Maybe (Spacing a)
    forall a b. (a -> b) -> a -> b
    $ Spacing a
    s { screenBorderEnabled = f sbe }
            | Just (ModifyWindowBorder Border -> Border
    f) <- SomeMessage -> Maybe SpacingModifier
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m
            = Spacing a -> Maybe (Spacing a)
    forall a. a -> Maybe a
    Just (Spacing a -> Maybe (Spacing a)) -> Spacing a -> Maybe (Spacing a)
    forall a b. (a -> b) -> a -> b
    $ Spacing a
    s { windowBorder = f wb }
            | Just (ModifyWindowBorderEnabled Bool -> Bool
    f) <- SomeMessage -> Maybe SpacingModifier
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m
            = Spacing a -> Maybe (Spacing a)
    forall a. a -> Maybe a
    Just (Spacing a -> Maybe (Spacing a)) -> Spacing a -> Maybe (Spacing a)
    forall a b. (a -> b) -> a -> b
    $ Spacing a
    s { windowBorderEnabled = f wbe }
            | Bool
    otherwise
            = Maybe (Spacing a)
    forall a. Maybe a
    Nothing

        modifierDescription :: Spacing a -> String
    modifierDescription Spacing {} =
            String
    "Spacing"

    -- | Generate the 'ModifiedLayout', exposing all initial state of 'Spacing'.
    spacingRaw :: Bool     -- ^ The 'smartBorder'.
               -> Border   -- ^ The 'screenBorder'.
               -> Bool     -- ^ The 'screenBorderEnabled'.
               -> Border   -- ^ The 'windowBorder'.
               -> Bool     -- ^ The 'windowBorderEnabled'.
               -> l a -> ModifiedLayout Spacing l a
    spacingRaw :: forall (l :: * -> *) a.
    Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    spacingRaw Bool
    b Border
    sb Bool
    sbe Border
    wb Bool
    wbe = Spacing a -> l a -> ModifiedLayout Spacing l a
    forall (m :: * -> *) (l :: * -> *) a.
    m a -> l a -> ModifiedLayout m l a
    ModifiedLayout (Bool -> Border -> Bool -> Border -> Bool -> Spacing a
    forall a. Bool -> Border -> Bool -> Border -> Bool -> Spacing a
    Spacing Bool
    b Border
    sb Bool
    sbe Border
    wb Bool
    wbe)

    -- | Messages to alter the state of 'Spacing' using the endomorphic function
    -- arguments.
    data SpacingModifier
        = ModifySmartBorder (Bool -> Bool)
        | ModifyScreenBorder (Border -> Border)
        | ModifyScreenBorderEnabled (Bool -> Bool)
        | ModifyWindowBorder (Border -> Border)
        | ModifyWindowBorderEnabled (Bool -> Bool)

    instance Message SpacingModifier

    -- | Set 'smartBorder' to the given 'Bool'.
    setSmartSpacing :: Bool -> X ()
    setSmartSpacing :: Bool -> X ()
    setSmartSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Bool -> SpacingModifier) -> Bool -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Bool -> Bool) -> SpacingModifier
    ModifySmartBorder ((Bool -> Bool) -> SpacingModifier)
    -> (Bool -> Bool -> Bool) -> Bool -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Bool -> Bool
    forall a b. a -> b -> a
    const

    -- | Set 'screenBorder' to the given 'Border'.
    setScreenSpacing :: Border -> X ()
    setScreenSpacing :: Border -> X ()
    setScreenSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Border -> SpacingModifier) -> Border -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Border -> Border) -> SpacingModifier
    ModifyScreenBorder ((Border -> Border) -> SpacingModifier)
    -> (Border -> Border -> Border) -> Border -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Border -> Border -> Border
    forall a b. a -> b -> a
    const

    -- | Set 'screenBorderEnabled' to the given 'Bool'.
    setScreenSpacingEnabled :: Bool -> X ()
    setScreenSpacingEnabled :: Bool -> X ()
    setScreenSpacingEnabled = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Bool -> SpacingModifier) -> Bool -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Bool -> Bool) -> SpacingModifier
    ModifyScreenBorderEnabled ((Bool -> Bool) -> SpacingModifier)
    -> (Bool -> Bool -> Bool) -> Bool -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Bool -> Bool
    forall a b. a -> b -> a
    const

    -- | Set 'windowBorder' to the given 'Border'.
    setWindowSpacing :: Border -> X ()
    setWindowSpacing :: Border -> X ()
    setWindowSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Border -> SpacingModifier) -> Border -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Border -> Border) -> SpacingModifier
    ModifyWindowBorder ((Border -> Border) -> SpacingModifier)
    -> (Border -> Border -> Border) -> Border -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Border -> Border -> Border
    forall a b. a -> b -> a
    const

    -- | Set 'windowBorderEnabled' to the given 'Bool'.
    setWindowSpacingEnabled :: Bool -> X ()
    setWindowSpacingEnabled :: Bool -> X ()
    setWindowSpacingEnabled = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Bool -> SpacingModifier) -> Bool -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Bool -> Bool) -> SpacingModifier
    ModifyWindowBorderEnabled ((Bool -> Bool) -> SpacingModifier)
    -> (Bool -> Bool -> Bool) -> Bool -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Bool -> Bool
    forall a b. a -> b -> a
    const

    -- | Toggle 'smartBorder'.
    toggleSmartSpacing :: X ()
    toggleSmartSpacing :: X ()
    toggleSmartSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ()) -> SpacingModifier -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Bool -> Bool) -> SpacingModifier
    ModifySmartBorder Bool -> Bool
    not

    -- | Toggle 'screenBorderEnabled'.
    toggleScreenSpacingEnabled :: X ()
    toggleScreenSpacingEnabled :: X ()
    toggleScreenSpacingEnabled = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ()) -> SpacingModifier -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Bool -> Bool) -> SpacingModifier
    ModifyScreenBorderEnabled Bool -> Bool
    not

    -- | Toggle 'windowBorderEnabled'.
    toggleWindowSpacingEnabled :: X ()
    toggleWindowSpacingEnabled :: X ()
    toggleWindowSpacingEnabled = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ()) -> SpacingModifier -> X ()
    forall a b. (a -> b) -> a -> b
    $ (Bool -> Bool) -> SpacingModifier
    ModifyWindowBorderEnabled Bool -> Bool
    not

    -- | Set all borders to a uniform size; see 'setWindowSpacing' and
    -- 'setScreenSpacing'.
    setScreenWindowSpacing :: Integer -> X ()
    setScreenWindowSpacing :: Integer -> X ()
    setScreenWindowSpacing = [SpacingModifier] -> X ()
    forall a. Message a => [a] -> X ()
    sendMessages ([SpacingModifier] -> X ())
    -> (Integer -> [SpacingModifier]) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((((Border -> Border) -> SpacingModifier) -> SpacingModifier)
     -> [(Border -> Border) -> SpacingModifier] -> [SpacingModifier])
    -> [(Border -> Border) -> SpacingModifier]
    -> (((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> [SpacingModifier]
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip (((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> [(Border -> Border) -> SpacingModifier] -> [SpacingModifier]
    forall a b. (a -> b) -> [a] -> [b]
    map [(Border -> Border) -> SpacingModifier
    ModifyWindowBorder,(Border -> Border) -> SpacingModifier
    ModifyScreenBorder]
                                          ((((Border -> Border) -> SpacingModifier) -> SpacingModifier)
     -> [SpacingModifier])
    -> (Integer
        -> ((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> Integer
    -> [SpacingModifier]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (((Border -> Border) -> SpacingModifier)
     -> (Border -> Border) -> SpacingModifier)
    -> (Border -> Border)
    -> ((Border -> Border) -> SpacingModifier)
    -> SpacingModifier
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ((Border -> Border) -> SpacingModifier)
    -> (Border -> Border) -> SpacingModifier
    forall a. a -> a
    id ((Border -> Border)
     -> ((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> (Integer -> Border -> Border)
    -> Integer
    -> ((Border -> Border) -> SpacingModifier)
    -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Border -> Border -> Border
    forall a b. a -> b -> a
    const (Border -> Border -> Border)
    -> (Integer -> Border) -> Integer -> Border -> Border
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Border
    uniformBorder

    -- | Increment the borders of 'windowBorder' using 'borderIncrementBy', which
    -- preserves border ratios during clamping.
    incWindowSpacing :: Integer -> X ()
    incWindowSpacing :: Integer -> X ()
    incWindowSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Integer -> SpacingModifier) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Border -> Border) -> SpacingModifier
    ModifyWindowBorder ((Border -> Border) -> SpacingModifier)
    -> (Integer -> Border -> Border) -> Integer -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Border -> Border
    borderIncrementBy

    -- | Increment the borders of 'screenBorder' using 'borderIncrementBy'.
    incScreenSpacing :: Integer -> X ()
    incScreenSpacing :: Integer -> X ()
    incScreenSpacing = SpacingModifier -> X ()
    forall a. Message a => a -> X ()
    sendMessage (SpacingModifier -> X ())
    -> (Integer -> SpacingModifier) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Border -> Border) -> SpacingModifier
    ModifyScreenBorder ((Border -> Border) -> SpacingModifier)
    -> (Integer -> Border -> Border) -> Integer -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Border -> Border
    borderIncrementBy

    -- | Inverse of 'incWindowSpacing', equivalent to applying 'negate'.
    decWindowSpacing :: Integer -> X ()
    decWindowSpacing :: Integer -> X ()
    decWindowSpacing = Integer -> X ()
    incWindowSpacing (Integer -> X ()) -> (Integer -> Integer) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Integer
    forall a. Num a => a -> a
    negate

    -- | Inverse of 'incScreenSpacing'.
    decScreenSpacing :: Integer -> X ()
    decScreenSpacing :: Integer -> X ()
    decScreenSpacing = Integer -> X ()
    incScreenSpacing (Integer -> X ()) -> (Integer -> Integer) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Integer
    forall a. Num a => a -> a
    negate

    -- | Increment both screen and window borders; see 'incWindowSpacing' and
    -- 'incScreenSpacing'.
    incScreenWindowSpacing :: Integer -> X ()
    incScreenWindowSpacing :: Integer -> X ()
    incScreenWindowSpacing = [SpacingModifier] -> X ()
    forall a. Message a => [a] -> X ()
    sendMessages ([SpacingModifier] -> X ())
    -> (Integer -> [SpacingModifier]) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((((Border -> Border) -> SpacingModifier) -> SpacingModifier)
     -> [(Border -> Border) -> SpacingModifier] -> [SpacingModifier])
    -> [(Border -> Border) -> SpacingModifier]
    -> (((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> [SpacingModifier]
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip (((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> [(Border -> Border) -> SpacingModifier] -> [SpacingModifier]
    forall a b. (a -> b) -> [a] -> [b]
    map [(Border -> Border) -> SpacingModifier
    ModifyWindowBorder,(Border -> Border) -> SpacingModifier
    ModifyScreenBorder]
                                          ((((Border -> Border) -> SpacingModifier) -> SpacingModifier)
     -> [SpacingModifier])
    -> (Integer
        -> ((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> Integer
    -> [SpacingModifier]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (((Border -> Border) -> SpacingModifier)
     -> (Border -> Border) -> SpacingModifier)
    -> (Border -> Border)
    -> ((Border -> Border) -> SpacingModifier)
    -> SpacingModifier
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ((Border -> Border) -> SpacingModifier)
    -> (Border -> Border) -> SpacingModifier
    forall a. a -> a
    id ((Border -> Border)
     -> ((Border -> Border) -> SpacingModifier) -> SpacingModifier)
    -> (Integer -> Border -> Border)
    -> Integer
    -> ((Border -> Border) -> SpacingModifier)
    -> SpacingModifier
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Border -> Border
    borderIncrementBy

    -- | Inverse of 'incScreenWindowSpacing'.
    decScreenWindowSpacing :: Integer -> X ()
    decScreenWindowSpacing :: Integer -> X ()
    decScreenWindowSpacing = Integer -> X ()
    incScreenWindowSpacing (Integer -> X ()) -> (Integer -> Integer) -> Integer -> X ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Integer
    forall a. Num a => a -> a
    negate

    -- | Construct a uniform 'Border'. That is, having equal individual borders.
    uniformBorder :: Integer -> Border
    uniformBorder :: Integer -> Border
    uniformBorder Integer
    i = Integer -> Integer -> Integer -> Integer -> Border
    Border Integer
    i Integer
    i Integer
    i Integer
    i

    -- | Map a function over a 'Border'. That is, over the four individual borders.
    borderMap :: (Integer -> Integer) -> Border -> Border
    borderMap :: (Integer -> Integer) -> Border -> Border
    borderMap Integer -> Integer
    f (Border Integer
    t Integer
    b Integer
    r Integer
    l) = Integer -> Integer -> Integer -> Integer -> Border
    Border (Integer -> Integer
    f Integer
    t) (Integer -> Integer
    f Integer
    b) (Integer -> Integer
    f Integer
    r) (Integer -> Integer
    f Integer
    l)

    -- | Clamp borders to within @[0,Infinity]@.
    borderClampGTZero :: Border -> Border
    borderClampGTZero :: Border -> Border
    borderClampGTZero = (Integer -> Integer) -> Border -> Border
    borderMap (Integer -> Integer -> Integer
    forall a. Ord a => a -> a -> a
    max Integer
    0)

    -- | Change the border spacing by the provided amount, adjusted so that at
    -- least one border field is @>=0@.
    borderIncrementBy :: Integer -> Border -> Border
    borderIncrementBy :: Integer -> Border -> Border
    borderIncrementBy Integer
    i (Border Integer
    t Integer
    b Integer
    r Integer
    l) =
        let bl :: [Integer]
    bl = [Integer
    t,Integer
    b,Integer
    r,Integer
    l]
            o :: Integer
    o  = [Integer] -> Integer
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum [Integer]
    bl
            o' :: Integer
    o' = Integer -> Integer -> Integer
    forall a. Ord a => a -> a -> a
    max Integer
    i (Integer -> Integer) -> Integer -> Integer
    forall a b. (a -> b) -> a -> b
    $ Integer -> Integer
    forall a. Num a => a -> a
    negate Integer
    o
        in  Integer -> Integer -> Integer -> Integer -> Border
    Border (Integer
    t Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + Integer
    o') (Integer
    b Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + Integer
    o') (Integer
    r Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + Integer
    o') (Integer
    l Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + Integer
    o')

    -- | Interface to 'XMonad.Util.Rectangle.withBorder'.
    withBorder' :: Border -> Integer -> Rectangle -> Rectangle
    withBorder' :: Border -> Integer -> Rectangle -> Rectangle
    withBorder' (Border Integer
    t Integer
    b Integer
    r Integer
    l) = Integer
    -> Integer
    -> Integer
    -> Integer
    -> Integer
    -> Rectangle
    -> Rectangle
    R.withBorder Integer
    t Integer
    b Integer
    r Integer
    l

    -- | Return the border necessary to derive the second rectangle from the first.
    -- Since 'R.withBorder' may scale the borders to stay within rectangle bounds,
    -- it is not an invertible operation, i.e. applying a negated border may not
    -- return the original rectangle. Use this instead.
    toBorder :: Rectangle -> Rectangle -> Border
    toBorder :: Rectangle -> Rectangle -> Border
    toBorder Rectangle
    r1 Rectangle
    r2 =
        let R.PointRectangle Integer
    r1_x1 Integer
    r1_y1 Integer
    r1_x2 Integer
    r1_y2 = Rectangle -> PointRectangle Integer
    R.pixelsToCoordinates Rectangle
    r1
            R.PointRectangle Integer
    r2_x1 Integer
    r2_y1 Integer
    r2_x2 Integer
    r2_y2 = Rectangle -> PointRectangle Integer
    R.pixelsToCoordinates Rectangle
    r2
            l :: Integer
    l = Integer
    r2_x1 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    - Integer
    r1_x1
            r :: Integer
    r = Integer
    r1_x2 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    - Integer
    r2_x2
            t :: Integer
    t = Integer
    r2_y1 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    - Integer
    r1_y1
            b :: Integer
    b = Integer
    r1_y2 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    - Integer
    r2_y2
        in Integer -> Integer -> Integer -> Integer -> Border
    Border Integer
    t Integer
    b Integer
    r Integer
    l

    -- | Given an ordering and a three-tuple, return the first tuple entry if 'LT',
    -- second if 'EQ' and third if 'GT'.
    orderSelect :: Ordering -> (a,a,a) -> a
    orderSelect :: forall a. Ordering -> (a, a, a) -> a
    orderSelect Ordering
    o (a
    lt,a
    eq,a
    gt) = case Ordering
    o of
        Ordering
    LT -> a
    lt
        Ordering
    EQ -> a
    eq
        Ordering
    GT -> a
    gt

    -----------------------------------------------------------------------------
    -- Backwards Compatibility:
    -----------------------------------------------------------------------------

    -- | Surround all windows by a certain number of pixels of blank space. See
    -- 'spacingRaw'.
    spacing :: Int -> l a -> ModifiedLayout Spacing l a
    spacing :: forall (l :: * -> *) a. Int -> l a -> ModifiedLayout Spacing l a
    spacing Int
    i = Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    forall (l :: * -> *) a.
    Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    spacingRaw Bool
    False (Integer -> Border
    uniformBorder Integer
    0) Bool
    False (Integer -> Border
    uniformBorder Integer
    i') Bool
    True
        where i' :: Integer
    i' = Int -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    i

    -- | Surround all windows by a certain number of pixels of blank space, and
    -- additionally adds the same amount of spacing around the edge of the screen.
    -- See 'spacingRaw'.
    spacingWithEdge :: Int -> l a -> ModifiedLayout Spacing l a
    spacingWithEdge :: forall (l :: * -> *) a. Int -> l a -> ModifiedLayout Spacing l a
    spacingWithEdge Int
    i = Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    forall (l :: * -> *) a.
    Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    spacingRaw Bool
    False (Integer -> Border
    uniformBorder Integer
    i') Bool
    True (Integer -> Border
    uniformBorder Integer
    i') Bool
    True
        where i' :: Integer
    i' = Int -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    i

    -- | Surrounds all windows with blank space, except when the window is the only
    -- visible window on the current workspace. See 'spacingRaw'.
    smartSpacing :: Int -> l a -> ModifiedLayout Spacing l a
    smartSpacing :: forall (l :: * -> *) a. Int -> l a -> ModifiedLayout Spacing l a
    smartSpacing Int
    i = Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    forall (l :: * -> *) a.
    Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    spacingRaw Bool
    True (Integer -> Border
    uniformBorder Integer
    0) Bool
    False (Integer -> Border
    uniformBorder Integer
    i') Bool
    True
        where i' :: Integer
    i' = Int -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    i

    -- | Surrounds all windows with blank space, and adds the same amount of
    -- spacing around the edge of the screen, except when the window is the only
    -- visible window on the current workspace. See 'spacingRaw'.
    smartSpacingWithEdge :: Int -> l a -> ModifiedLayout Spacing l a
    smartSpacingWithEdge :: forall (l :: * -> *) a. Int -> l a -> ModifiedLayout Spacing l a
    smartSpacingWithEdge Int
    i = Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    forall (l :: * -> *) a.
    Bool
    -> Border
    -> Bool
    -> Border
    -> Bool
    -> l a
    -> ModifiedLayout Spacing l a
    spacingRaw Bool
    True (Integer -> Border
    uniformBorder Integer
    i') Bool
    True (Integer -> Border
    uniformBorder Integer
    i') Bool
    True
        where i' :: Integer
    i' = Int -> Integer
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    i