{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.CenterMainFluid
-- Description :  Three column layout with master in center and unoccupied spaces reserved.
-- Copyright   :  (c) 2023 Mahdi Seyedan
-- License     :  BSD-style (see xmonad/LICENSE)
--
-- Maintainer  :  Mahdi Seyedan. <mahdisn78@gmail.com>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A three column layout with main column in the center and
-- two stack columns surrounding it. There will be always
-- a pane in the center column and unoccupied spaces on the
-- sides are reserved.
-- It's best suited for ultrawide montiors, where a single
-- stretched window might be annoying.
-----------------------------------------------------------------------------

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

import XMonad
import qualified XMonad.StackSet as W
import Control.Monad (msum)

-- $usage
-- You can use this module by adding following in your @xmonad.hs@:
--
-- > import XMonad.Layout.CenterMainFluid
--
-- Then edit your @layoutHook@ by adding the CenterMainFluid layout:
--
-- > myLayoutHook = CenterMainFluid 1 (3/100) (70/100) ||| ...
-- > main = xmonad def { layoutHook = myLayout }
--
-- The first argument specifies how many windows initially appear in the center
-- column. The second argument specifies the amount to resize while resizing
-- and the third argument specifies the initial size of the center column.
--
-- 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".


-- | Arguments are nmaster, delta, fraction. Supports 'Shrink', 'Expand' and
-- 'IncMasterN'
data CenterMainFluid a = CenterMainFluid
  { forall a. CenterMainFluid a -> Int
cmfNMaster :: !Int             -- ^ The default number of windows in the center pane (default: 1)
  , forall a. CenterMainFluid a -> Rational
cmfRatioIncrement :: !Rational -- ^ Percent of screen to increment by when resizing panes (default: 3/100)
  , forall a. CenterMainFluid a -> Rational
cmfRatio :: !Rational          -- ^ Default proportion of screen occupied by the center pane (default: 70/100)
  }
  deriving (Int -> CenterMainFluid a -> ShowS
[CenterMainFluid a] -> ShowS
CenterMainFluid a -> String
(Int -> CenterMainFluid a -> ShowS)
-> (CenterMainFluid a -> String)
-> ([CenterMainFluid a] -> ShowS)
-> Show (CenterMainFluid a)
forall a. Int -> CenterMainFluid a -> ShowS
forall a. [CenterMainFluid a] -> ShowS
forall a. CenterMainFluid a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Int -> CenterMainFluid a -> ShowS
showsPrec :: Int -> CenterMainFluid a -> ShowS
$cshow :: forall a. CenterMainFluid a -> String
show :: CenterMainFluid a -> String
$cshowList :: forall a. [CenterMainFluid a] -> ShowS
showList :: [CenterMainFluid a] -> ShowS
Show,ReadPrec [CenterMainFluid a]
ReadPrec (CenterMainFluid a)
Int -> ReadS (CenterMainFluid a)
ReadS [CenterMainFluid a]
(Int -> ReadS (CenterMainFluid a))
-> ReadS [CenterMainFluid a]
-> ReadPrec (CenterMainFluid a)
-> ReadPrec [CenterMainFluid a]
-> Read (CenterMainFluid a)
forall a. ReadPrec [CenterMainFluid a]
forall a. ReadPrec (CenterMainFluid a)
forall a. Int -> ReadS (CenterMainFluid a)
forall a. ReadS [CenterMainFluid a]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: forall a. Int -> ReadS (CenterMainFluid a)
readsPrec :: Int -> ReadS (CenterMainFluid a)
$creadList :: forall a. ReadS [CenterMainFluid a]
readList :: ReadS [CenterMainFluid a]
$creadPrec :: forall a. ReadPrec (CenterMainFluid a)
readPrec :: ReadPrec (CenterMainFluid a)
$creadListPrec :: forall a. ReadPrec [CenterMainFluid a]
readListPrec :: ReadPrec [CenterMainFluid a]
Read)

instance LayoutClass CenterMainFluid a where

    pureLayout :: CenterMainFluid a -> Rectangle -> Stack a -> [(a, Rectangle)]
pureLayout (CenterMainFluid Int
nmaster Rational
_ Rational
frac) Rectangle
r Stack a
s
        | Rational
frac Rational -> Rational -> Bool
forall a. Eq a => a -> a -> Bool
== Rational
0 = Int -> [(a, Rectangle)] -> [(a, Rectangle)]
forall a. Int -> [a] -> [a]
drop Int
nmaster [(a, Rectangle)]
layout
        | Rational
frac Rational -> Rational -> Bool
forall a. Eq a => a -> a -> Bool
== Rational
1 = Int -> [(a, Rectangle)] -> [(a, Rectangle)]
forall a. Int -> [a] -> [a]
take Int
nmaster [(a, Rectangle)]
layout
        | Bool
otherwise = [(a, Rectangle)]
layout
      where layout :: [(a, Rectangle)]
layout = [a] -> [Rectangle] -> [(a, Rectangle)]
forall a b. [a] -> [b] -> [(a, b)]
zip [a]
ws [Rectangle]
rs
            ws :: [a]
ws = Stack a -> [a]
forall a. Stack a -> [a]
W.integrate Stack a
s
            rs :: [Rectangle]
rs = Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile3 Rational
frac Rectangle
r Int
nmaster ([a] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
ws)

    pureMessage :: CenterMainFluid a -> SomeMessage -> Maybe (CenterMainFluid a)
pureMessage (CenterMainFluid Int
nmaster Rational
delta Rational
frac) SomeMessage
m =
            [Maybe (CenterMainFluid a)] -> Maybe (CenterMainFluid a)
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum [(Resize -> CenterMainFluid a)
-> Maybe Resize -> Maybe (CenterMainFluid a)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Resize -> CenterMainFluid a
forall {a}. Resize -> CenterMainFluid a
resize     (SomeMessage -> Maybe Resize
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
m)
                 ,(IncMasterN -> CenterMainFluid a)
-> Maybe IncMasterN -> Maybe (CenterMainFluid a)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap IncMasterN -> CenterMainFluid a
forall {a}. IncMasterN -> CenterMainFluid a
incmastern (SomeMessage -> Maybe IncMasterN
forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
m)]

      where resize :: Resize -> CenterMainFluid a
resize Resize
Shrink             = Int -> Rational -> Rational -> CenterMainFluid a
forall a. Int -> Rational -> Rational -> CenterMainFluid a
CenterMainFluid Int
nmaster Rational
delta (Rational -> Rational -> Rational
forall a. Ord a => a -> a -> a
max Rational
0 (Rational -> Rational) -> Rational -> Rational
forall a b. (a -> b) -> a -> b
$ Rational
fracRational -> Rational -> Rational
forall a. Num a => a -> a -> a
-Rational
delta)
            resize Resize
Expand             = Int -> Rational -> Rational -> CenterMainFluid a
forall a. Int -> Rational -> Rational -> CenterMainFluid a
CenterMainFluid Int
nmaster Rational
delta (Rational -> Rational -> Rational
forall a. Ord a => a -> a -> a
min Rational
1 (Rational -> Rational) -> Rational -> Rational
forall a b. (a -> b) -> a -> b
$ Rational
fracRational -> Rational -> Rational
forall a. Num a => a -> a -> a
+Rational
delta)
            incmastern :: IncMasterN -> CenterMainFluid a
incmastern (IncMasterN Int
d) = Int -> Rational -> Rational -> CenterMainFluid a
forall a. Int -> Rational -> Rational -> CenterMainFluid a
CenterMainFluid (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)) Rational
delta Rational
frac

    description :: CenterMainFluid a -> String
description CenterMainFluid a
_ = String
"CenterMainFluid"

tile3 :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile3 :: Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile3 Rational
f Rectangle
r Int
nmaster Int
n
  | Int
nmaster Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 Bool -> Bool -> Bool
|| Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
nmaster = Int -> Rectangle -> [Rectangle]
splitVertically Int
n Rectangle
middleR
  | Bool
otherwise = [Rectangle]
masters [Rectangle] -> [Rectangle] -> [Rectangle]
forall a. [a] -> [a] -> [a]
++ [Rectangle]
rights [Rectangle] -> [Rectangle] -> [Rectangle]
forall a. [a] -> [a] -> [a]
++ [Rectangle]
lefts
      where (Rectangle
leftR, Rectangle
middleR, Rectangle
rightR) = Rational -> Rectangle -> (Rectangle, Rectangle, Rectangle)
forall r.
RealFrac r =>
r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
split3HorizontallyBy Rational
f Rectangle
r
            (Int
halfN, Int
remaining) = (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)
`divMod` Int
2
            masters :: [Rectangle]
masters = Int -> Rectangle -> [Rectangle]
splitVertically Int
nmaster Rectangle
middleR
            lefts :: [Rectangle]
lefts = Int -> Rectangle -> [Rectangle]
splitVertically Int
halfN Rectangle
leftR
            rights :: [Rectangle]
rights = Int -> Rectangle -> [Rectangle]
splitVertically (Int
halfN Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
remaining) Rectangle
rightR

-- | Divide the screen into three rectangles, using a rational to specify the ratio of center one
split3HorizontallyBy :: RealFrac r => r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
split3HorizontallyBy :: forall r.
RealFrac r =>
r -> Rectangle -> (Rectangle, Rectangle, Rectangle)
split3HorizontallyBy r
f (Rectangle Position
sx Position
sy Dimension
sw Dimension
sh) =
  ( Position -> Position -> Dimension -> Dimension -> Rectangle
Rectangle Position
sx Position
sy Dimension
sidew 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
sidew) Position
sy Dimension
middlew 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
sidew Position -> Position -> Position
forall a. Num a => a -> a -> a
+ Dimension -> Position
forall a b. (Integral a, Num b) => a -> b
fromIntegral Dimension
middlew) Position
sy Dimension
sidew Dimension
sh
  )
  where middlew :: Dimension
middlew = r -> Dimension
forall b. Integral b => r -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor (r -> Dimension) -> r -> Dimension
forall a b. (a -> b) -> a -> b
$ Dimension -> r
forall a b. (Integral a, Num b) => a -> b
fromIntegral Dimension
sw r -> r -> r
forall a. Num a => a -> a -> a
* r
f
        sidew :: Dimension
sidew = (Dimension
sw Dimension -> Dimension -> Dimension
forall a. Num a => a -> a -> a
- Dimension -> Dimension
forall a b. (Integral a, Num b) => a -> b
fromIntegral Dimension
middlew) Dimension -> Dimension -> Dimension
forall a. Integral a => a -> a -> a
`div` Dimension
2