    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  XMonad.Layout.ThreeColumns
    -- Description :  A layout similar to @Tall@, but with three columns.
    -- Copyright   :  (c) Kai Grossjohann <kai@emptydomain.de>
    -- License     :  BSD3-style (see LICENSE)
    --
    -- Maintainer  :  ?
    -- Stability   :  unstable
    -- Portability :  unportable
    --
    -- A layout similar to tall but with three columns. With 2560x1600 pixels this
    -- layout can be used for a huge main window and up to six reasonable sized
    -- slave windows.
    -----------------------------------------------------------------------------

    module XMonad.Layout.ThreeColumns (
                                  -- * Usage
                                  -- $usage

                                  -- * Screenshots
                                  -- $screenshot
                                  ThreeCol(..)
                                 ) where

    import XMonad
    import XMonad.Prelude
    import qualified XMonad.StackSet as W

    import Data.Ratio

    -- $usage
    -- You can use this module with the following in your @xmonad.hs@:
    --
    -- > import XMonad.Layout.ThreeColumns
    --
    -- Then edit your @layoutHook@ by adding the ThreeCol layout:
    --
    -- > myLayout = ThreeCol 1 (3/100) (1/2) ||| ThreeColMid 1 (3/100) (1/2) ||| etc..
    -- > main = xmonad def { layoutHook = myLayout }
    --
    -- The first argument specifies how many windows initially appear in the main
    -- window. The second argument argument specifies the amount to resize while
    -- resizing and the third argument specifies the initial size of the columns.
    -- A positive size designates the fraction of the screen that the main window
    -- should occupy, but if the size is negative the absolute value designates the
    -- fraction a stack column should occupy. If both stack columns are visible,
    -- they always occupy the same amount of space.
    --
    -- The ThreeColMid variant places the main window between the stack columns.
    --
    -- For more detailed instructions on editing the layoutHook see
    -- <https://xmonad.org/TUTORIAL.html#customizing-xmonad the tutorial> and
    -- "XMonad.Doc.Extending#Editing_the_layout_hook".

    -- $screenshot
    -- <<https://user-images.githubusercontent.com/50166980/156938482-ac38fdd7-eb94-4371-801b-e191cdb9a4ba.png>>

    -- | Arguments are nmaster, delta, fraction
    data ThreeCol a = ThreeColMid { forall a. ThreeCol a -> Int
    threeColNMaster :: !Int, forall a. ThreeCol a -> Rational
    threeColDelta :: !Rational, forall a. ThreeCol a -> Rational
    threeColFrac :: !Rational}
                    | ThreeCol    { threeColNMaster :: !Int, threeColDelta :: !Rational, threeColFrac :: !Rational}
        deriving (Int -> ThreeCol a -> ShowS
    [ThreeCol a] -> ShowS
    ThreeCol a -> String
    (Int -> ThreeCol a -> ShowS)
    -> (ThreeCol a -> String)
    -> ([ThreeCol a] -> ShowS)
    -> Show (ThreeCol a)
    forall a. Int -> ThreeCol a -> ShowS
    forall a. [ThreeCol a] -> ShowS
    forall a. ThreeCol a -> String
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: forall a. Int -> ThreeCol a -> ShowS
    showsPrec :: Int -> ThreeCol a -> ShowS
    $cshow :: forall a. ThreeCol a -> String
    show :: ThreeCol a -> String
    $cshowList :: forall a. [ThreeCol a] -> ShowS
    showList :: [ThreeCol a] -> ShowS
    Show,ReadPrec [ThreeCol a]
    ReadPrec (ThreeCol a)
    Int -> ReadS (ThreeCol a)
    ReadS [ThreeCol a]
    (Int -> ReadS (ThreeCol a))
    -> ReadS [ThreeCol a]
    -> ReadPrec (ThreeCol a)
    -> ReadPrec [ThreeCol a]
    -> Read (ThreeCol a)
    forall a. ReadPrec [ThreeCol a]
    forall a. ReadPrec (ThreeCol a)
    forall a. Int -> ReadS (ThreeCol a)
    forall a. ReadS [ThreeCol a]
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: forall a. Int -> ReadS (ThreeCol a)
    readsPrec :: Int -> ReadS (ThreeCol a)
    $creadList :: forall a. ReadS [ThreeCol a]
    readList :: ReadS [ThreeCol a]
    $creadPrec :: forall a. ReadPrec (ThreeCol a)
    readPrec :: ReadPrec (ThreeCol a)
    $creadListPrec :: forall a. ReadPrec [ThreeCol a]
    readListPrec :: ReadPrec [ThreeCol a]
    Read)

    instance LayoutClass ThreeCol a where
        pureLayout :: ThreeCol a -> Rectangle -> Stack a -> [(a, Rectangle)]
    pureLayout (ThreeCol Int
    n Rational
    _ Rational
    f) Rectangle
    r    = Bool -> Int -> Rational -> Rectangle -> Stack a -> [(a, Rectangle)]
    forall a.
    Bool -> Int -> Rational -> Rectangle -> Stack a -> [(a, Rectangle)]
    doL Bool
    False Int
    n Rational
    f Rectangle
    r
        pureLayout (ThreeColMid Int
    n Rational
    _ Rational
    f) Rectangle
    r = Bool -> Int -> Rational -> Rectangle -> Stack a -> [(a, Rectangle)]
    forall a.
    Bool -> Int -> Rational -> Rectangle -> Stack a -> [(a, Rectangle)]
    doL Bool
    True Int
    n Rational
    f Rectangle
    r
        handleMessage :: ThreeCol a -> SomeMessage -> X (Maybe (ThreeCol a))
    handleMessage ThreeCol a
    l SomeMessage
    m =
            Maybe (ThreeCol a) -> X (Maybe (ThreeCol a))
    forall a. a -> X a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe (ThreeCol a) -> X (Maybe (ThreeCol a)))
    -> Maybe (ThreeCol a) -> X (Maybe (ThreeCol a))
    forall a b. (a -> b) -> a -> b
    $ [Maybe (ThreeCol a)] -> Maybe (ThreeCol a)
    forall (t :: * -> *) (m :: * -> *) a.
    (Foldable t, MonadPlus m) =>
    t (m a) -> m a
    msum [(Resize -> ThreeCol a) -> Maybe Resize -> Maybe (ThreeCol a)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Resize -> ThreeCol a
    forall {a}. Resize -> ThreeCol a
    resize     (SomeMessage -> Maybe Resize
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)
                          ,(IncMasterN -> ThreeCol a)
    -> Maybe IncMasterN -> Maybe (ThreeCol a)
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap IncMasterN -> ThreeCol a
    forall {a}. IncMasterN -> ThreeCol a
    incmastern (SomeMessage -> Maybe IncMasterN
    forall m. Message m => SomeMessage -> Maybe m
    fromMessage SomeMessage
    m)]
                where resize :: Resize -> ThreeCol a
    resize Resize
    Shrink = ThreeCol a
    l { threeColFrac = max (-0.5) $ f-d }
                      resize Resize
    Expand = ThreeCol a
    l { threeColFrac = min 1 $ f+d }
                      incmastern :: IncMasterN -> ThreeCol a
    incmastern (IncMasterN Int
    x) = ThreeCol a
    l { threeColNMaster = max 0 (n+x) }
                      n :: Int
    n = ThreeCol a -> Int
    forall a. ThreeCol a -> Int
    threeColNMaster ThreeCol a
    l
                      d :: Rational
    d = ThreeCol a -> Rational
    forall a. ThreeCol a -> Rational
    threeColDelta ThreeCol a
    l
                      f :: Rational
    f = ThreeCol a -> Rational
    forall a. ThreeCol a -> Rational
    threeColFrac ThreeCol a
    l
        description :: ThreeCol a -> String
    description ThreeCol a
    _ = String
    "ThreeCol"

    doL :: Bool-> Int-> Rational-> Rectangle-> W.Stack a-> [(a, Rectangle)]
    doL :: forall a.
    Bool -> Int -> Rational -> Rectangle -> Stack a -> [(a, Rectangle)]
    doL Bool
    m Int
    n Rational
    f Rectangle
    r = ([a] -> [Rectangle] -> [(a, Rectangle)])
    -> ([a] -> [Rectangle]) -> [a] -> [(a, Rectangle)]
    forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
    ap [a] -> [Rectangle] -> [(a, Rectangle)]
    forall a b. [a] -> [b] -> [(a, b)]
    zip (Bool -> Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 Bool
    m Rational
    f Rectangle
    r Int
    n (Int -> [Rectangle]) -> ([a] -> Int) -> [a] -> [Rectangle]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [a] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length) ([a] -> [(a, Rectangle)])
    -> (Stack a -> [a]) -> Stack a -> [(a, Rectangle)]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Stack a -> [a]
    forall a. Stack a -> [a]
    W.integrate

    -- | tile3.  Compute window positions using 3 panes
    tile3 :: Bool -> Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 :: Bool -> Rational -> Rectangle -> Int -> Int -> [Rectangle]
    tile3 Bool
    middle Rational
    f Rectangle
    r Int
    nmaster Int
    n
        | Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    nmaster Bool -> Bool -> Bool
    || Int
    nmaster Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0 = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    n Rectangle
    r
        | Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    nmasterInt -> Int -> Int
    forall a. Num a => a -> a -> a
    +Int
    1 = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    nmaster Rectangle
    s1 [Rectangle] -> [Rectangle] -> [Rectangle]
    forall a. [a] -> [a] -> [a]
    ++ Int -> Rectangle -> [Rectangle]
    splitVertically (Int
    nInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    nmaster) Rectangle
    s2
        | Bool
    otherwise = Int -> Rectangle -> [Rectangle]
    splitVertically Int
    nmaster Rectangle
    r1 [Rectangle] -> [Rectangle] -> [Rectangle]
    forall a. [a] -> [a] -> [a]
    ++ Int -> Rectangle -> [Rectangle]
    splitVertically Int
    nslave1 Rectangle
    r2 [Rectangle] -> [Rectangle] -> [Rectangle]
    forall a. [a] -> [a] -> [a]
    ++ Int -> Rectangle -> [Rectangle]
    splitVertically Int
    nslave2 Rectangle
    r3
            where (Rectangle
    r1, Rectangle
    r2, Rectangle
    r3) = Bool -> Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy Bool
    middle (if Rational
    fRational -> Rational -> Bool
    forall a. Ord a => a -> a -> Bool
    <Rational
    0 then Rational
    1Rational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    +Rational
    2Rational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    *Rational
    f else Rational
    f) Rectangle
    r
                  (Rectangle
    s1, Rectangle
    s2) = Rational -> Rectangle -> (Rectangle, Rectangle)
    forall r. RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
    splitHorizontallyBy (if Rational
    fRational -> Rational -> Bool
    forall a. Ord a => a -> a -> Bool
    <Rational
    0 then Rational
    1Rational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    +Rational
    f else Rational
    f) Rectangle
    r
                  nslave :: Int
    nslave = Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    nmaster
                  nslave1 :: Int
    nslave1 = Ratio Int -> Int
    forall b. Integral b => Ratio Int -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    ceiling (Int
    nslave Int -> Int -> Ratio Int
    forall a. Integral a => a -> a -> Ratio a
    % Int
    2)
                  nslave2 :: Int
    nslave2 = Int
    n Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    nmaster Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    nslave1

    split3HorizontallyBy :: Bool -> Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy :: Bool -> Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
    split3HorizontallyBy Bool
    middle Rational
    f (Rectangle Position
    sx Position
    sy Dimension
    sw Dimension
    sh) =
        if Bool
    middle
        then ( Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r3w) Position
    sy Dimension
    r1w Dimension
    sh
             , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle Position
    sx Position
    sy Dimension
    r3w Dimension
    sh
             , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r3w Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r1w) Position
    sy Dimension
    r2w Dimension
    sh )
        else ( Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle Position
    sx Position
    sy Dimension
    r1w Dimension
    sh
             , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r1w) Position
    sy Dimension
    r2w Dimension
    sh
             , Position -> Position -> Dimension -> Dimension -> Rectangle
    Rectangle (Position
    sx Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r1w Position -> Position -> Position
    forall a. Num a => a -> a -> a
    + Dimension -> Position
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    r2w) Position
    sy Dimension
    r3w Dimension
    sh )
            where r1w :: Dimension
    r1w = Rational -> Dimension
    forall b. Integral b => Rational -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    ceiling (Rational -> Dimension) -> Rational -> Dimension
    forall a b. (a -> b) -> a -> b
    $ Dimension -> Rational
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Dimension
    sw Rational -> Rational -> Rational
    forall a. Num a => a -> a -> a
    * Rational
    f
                  r2w :: Dimension
    r2w = Ratio Dimension -> Dimension
    forall b. Integral b => Ratio Dimension -> b
    forall a b. (RealFrac a, Integral b) => a -> b
    ceiling ( (Dimension
    sw Dimension -> Dimension -> Dimension
    forall a. Num a => a -> a -> a
    - Dimension
    r1w) Dimension -> Dimension -> Ratio Dimension
    forall a. Integral a => a -> a -> Ratio a
    % Dimension
    2 )
                  r3w :: Dimension
    r3w = Dimension
    sw Dimension -> Dimension -> Dimension
    forall a. Num a => a -> a -> a
    - Dimension
    r1w Dimension -> Dimension -> Dimension
    forall a. Num a => a -> a -> a
    - Dimension
    r2w