{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.MultiDishes
-- Description :  A layout stacking groups of extra windows underneath the master windows.
-- Copyright   :  (c) Jeremy Apthorp, Nathan Fairhurst
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Nathan Fairhurst <nathan.p3pictures@gmail.com>
-- Stability   :  unstable
-- Portability :  portable
--
-- MultiDishes is a layout that stacks groups of extra windows underneath
-- the master windows.
--
-----------------------------------------------------------------------------

module XMonad.Layout.MultiDishes (
                              -- * Usage
                              -- $usage
                              MultiDishes (..)
                            ) where

import XMonad
import XMonad.StackSet (integrate)
import XMonad.Prelude (ap)

-- $usage
-- You can use this module with the following in your @xmonad.hs@:
--
-- > import XMonad.Layout.MultiDishes
--
-- Then edit your @layoutHook@ by adding the MultiDishes layout:
--
-- > myLayout = MultiDishes 2 3 (1/6) ||| Full ||| etc..
-- > main = xmonad def { layoutHook = myLayout }
--
-- This is based on the Layout Dishes, but accepts another parameter for
-- the maximum number of dishes allowed within a stack.
--
-- > MultiDishes x 1 y
-- is equivalent to
-- > Dishes x y
--
-- The stack with the fewest dishes is always on top, so 4 windows
-- with the layout `MultiDishes 1 2 (1/5)` would look like this:
--
-- > _________
-- > |       |
-- > |   M   |
-- > |_______|
-- > |_______|
-- > |___|___|
--
-- 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".

data MultiDishes a = MultiDishes Int Int Rational deriving (Int -> MultiDishes a -> ShowS
[MultiDishes a] -> ShowS
MultiDishes a -> String
(Int -> MultiDishes a -> ShowS)
-> (MultiDishes a -> String)
-> ([MultiDishes a] -> ShowS)
-> Show (MultiDishes a)
forall a. Int -> MultiDishes a -> ShowS
forall a. [MultiDishes a] -> ShowS
forall a. MultiDishes a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Int -> MultiDishes a -> ShowS
showsPrec :: Int -> MultiDishes a -> ShowS
$cshow :: forall a. MultiDishes a -> String
show :: MultiDishes a -> String
$cshowList :: forall a. [MultiDishes a] -> ShowS
showList :: [MultiDishes a] -> ShowS
Show, ReadPrec [MultiDishes a]
ReadPrec (MultiDishes a)
Int -> ReadS (MultiDishes a)
ReadS [MultiDishes a]
(Int -> ReadS (MultiDishes a))
-> ReadS [MultiDishes a]
-> ReadPrec (MultiDishes a)
-> ReadPrec [MultiDishes a]
-> Read (MultiDishes a)
forall a. ReadPrec [MultiDishes a]
forall a. ReadPrec (MultiDishes a)
forall a. Int -> ReadS (MultiDishes a)
forall a. ReadS [MultiDishes a]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: forall a. Int -> ReadS (MultiDishes a)
readsPrec :: Int -> ReadS (MultiDishes a)
$creadList :: forall a. ReadS [MultiDishes a]
readList :: ReadS [MultiDishes a]
$creadPrec :: forall a. ReadPrec (MultiDishes a)
readPrec :: ReadPrec (MultiDishes a)
$creadListPrec :: forall a. ReadPrec [MultiDishes a]
readListPrec :: ReadPrec [MultiDishes a]
Read)
instance LayoutClass MultiDishes a where
    pureLayout :: MultiDishes a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout (MultiDishes Int
nmaster Int
dishesPerStack Rational
h) 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 (Rational -> Rectangle -> Int -> Int -> Int -> [Rectangle]
multiDishes Rational
h Rectangle
r Int
nmaster Int
dishesPerStack (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]
integrate
    pureMessage :: MultiDishes a -> SomeMessage -> Maybe (MultiDishes a)
pureMessage (MultiDishes Int
nmaster Int
dishesPerStack Rational
h) SomeMessage
m = (IncMasterN -> MultiDishes a)
-> Maybe IncMasterN -> Maybe (MultiDishes a)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap IncMasterN -> MultiDishes a
forall {a}. IncMasterN -> MultiDishes a
incmastern (SomeMessage -> Maybe IncMasterN
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
m)
        where incmastern :: IncMasterN -> MultiDishes a
incmastern (IncMasterN Int
d) = Int -> Int -> Rational -> MultiDishes a
forall a. Int -> Int -> Rational -> MultiDishes a
MultiDishes (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
nmasterInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
d)) Int
dishesPerStack Rational
h

multiDishes :: Rational -> Rectangle -> Int -> Int -> Int -> [Rectangle]
multiDishes :: Rational -> Rectangle -> Int -> Int -> Int -> [Rectangle]
multiDishes Rational
h Rectangle
s Int
nmaster Int
dishesPerStack Int
n = if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
nmaster
                        then Int -> Rectangle -> [Rectangle]
splitHorizontally Int
n Rectangle
s
                        else [Rectangle]
ws
 where
    (Int
filledDishStackCount, Int
remainder) =
      (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
nmaster) Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
`quotRem` Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
dishesPerStack

    (Int
firstDepth, Int
dishStackCount) =
      if Int
remainder Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 then
        (Int
dishesPerStack, Int
filledDishStackCount)
      else
        (Int
remainder, Int
filledDishStackCount Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

    (Rectangle
masterRect, Rectangle
dishesRect) =
      Rational -> Rectangle -> (Rectangle, Rectangle)
forall r. RealFrac r => r -> Rectangle -> (Rectangle, Rectangle)
splitVerticallyBy (Rational
1 Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
- Int -> Rational
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
dishStackCount Rational -> Rational -> Rational
forall a. Num a => a -> a -> a
* Rational
h) Rectangle
s

    dishStackRects :: [Rectangle]
dishStackRects =
      Int -> Rectangle -> [Rectangle]
splitVertically Int
dishStackCount Rectangle
dishesRect

    allDishRects :: [Rectangle]
allDishRects = case [Rectangle]
dishStackRects of
      (Rectangle
firstStack:[Rectangle]
bottomDishStacks) ->
        Int -> Rectangle -> [Rectangle]
splitHorizontally Int
firstDepth Rectangle
firstStack [Rectangle] -> [Rectangle] -> [Rectangle]
forall a. [a] -> [a] -> [a]
++ ([Rectangle]
bottomDishStacks [Rectangle] -> (Rectangle -> [Rectangle]) -> [Rectangle]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Int -> Rectangle -> [Rectangle]
splitHorizontally Int
dishesPerStack)
      [] -> []

    ws :: [Rectangle]
ws =
      Int -> Rectangle -> [Rectangle]
splitHorizontally Int
nmaster Rectangle
masterRect [Rectangle] -> [Rectangle] -> [Rectangle]
forall a. [a] -> [a] -> [a]
++ [Rectangle]
allDishRects