{-# 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

      -- * Backwards Compatibility
    , SpacingWithEdge
    , SmartSpacing, SmartSpacingWithEdge
    , ModifySpacing (..)
    , setSpacing, incSpacing
    ) 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\/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
showList :: [Border] -> ShowS
$cshowList :: [Border] -> ShowS
show :: Border -> String
$cshow :: Border -> String
showsPrec :: Int -> Border -> ShowS
$cshowsPrec :: Int -> 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
readListPrec :: ReadPrec [Border]
$creadListPrec :: ReadPrec [Border]
readPrec :: ReadPrec Border
$creadPrec :: ReadPrec Border
readList :: ReadS [Border]
$creadList :: ReadS [Border]
readsPrec :: Int -> ReadS Border
$creadsPrec :: Int -> ReadS 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
showList :: [Spacing a] -> ShowS
$cshowList :: forall a. [Spacing a] -> ShowS
show :: Spacing a -> String
$cshow :: forall a. Spacing a -> String
showsPrec :: Int -> Spacing a -> ShowS
$cshowsPrec :: forall a. Int -> 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
readListPrec :: ReadPrec [Spacing a]
$creadListPrec :: forall a. ReadPrec [Spacing a]
readPrec :: ReadPrec (Spacing a)
$creadPrec :: forall a. ReadPrec (Spacing a)
readList :: ReadS [Spacing a]
$creadList :: forall a. ReadS [Spacing a]
readsPrec :: Int -> ReadS (Spacing a)
$creadsPrec :: forall a. Int -> ReadS (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 (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 (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 (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 :: Position
rect_x = Position
x Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Integer -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
dx, rect_y :: Position
rect_y = Position
y Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Integer -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
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 (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 (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 :: Bool
smartBorder = Bool -> Bool
f Bool
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 :: Border
screenBorder = Border -> Border
f Border
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 :: Bool
screenBorderEnabled = Bool -> Bool
f Bool
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 :: Border
windowBorder = Border -> Border
f Border
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 :: Bool
windowBorderEnabled = Bool -> Bool
f Bool
wbe }
        | Just (ModifySpacing Int -> Int
f) <- SomeMessage -> Maybe ModifySpacing
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
$ let f' :: Border -> Border
f' = (Integer -> Integer) -> Border -> Border
borderMap (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Integer) -> (Integer -> Int) -> Integer -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
f (Int -> Int) -> (Integer -> Int) -> Integer -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral)
                 in  Spacing a
s { screenBorder :: Border
screenBorder = Border -> Border
f' Border
sb, windowBorder :: Border
windowBorder = Border -> Border
f' Border
wb }
        | 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 (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:
-----------------------------------------------------------------------------
{-# DEPRECATED SpacingWithEdge, SmartSpacing, SmartSpacingWithEdge "Use Spacing instead." #-}
{-# DEPRECATED ModifySpacing "Use SpacingModifier instead, perhaps with sendMessages." #-}
{-# DEPRECATED setSpacing "Use setScreenWindowSpacing instead." #-}
{-# DEPRECATED incSpacing "Use incScreenWindowSpacing instead." #-}

-- | A type synonym for the 'Spacing' 'LayoutModifier'.
type SpacingWithEdge = Spacing

-- | A type synonym for the 'Spacing' 'LayoutModifier'.
type SmartSpacing = Spacing

-- | A type synonym for the 'Spacing' 'LayoutModifier'.
type SmartSpacingWithEdge = Spacing

-- | Message to dynamically modify (e.g. increase\/decrease\/set) the size of
-- the screen spacing and window spacing. See 'SpacingModifier'.
newtype ModifySpacing = ModifySpacing (Int -> Int)

instance Message ModifySpacing

-- | 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

-- | See 'setScreenWindowSpacing'.
setSpacing :: Int -> X ()
setSpacing :: Int -> X ()
setSpacing = Integer -> X ()
setScreenWindowSpacing (Integer -> X ()) -> (Int -> Integer) -> Int -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | See 'incScreenWindowSpacing'.
incSpacing :: Int -> X ()
incSpacing :: Int -> X ()
incSpacing = Integer -> X ()
incScreenWindowSpacing (Integer -> X ()) -> (Int -> Integer) -> Int -> X ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral