{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Layout.FixedColumn
-- Description :  Like Tall, but split at a fixed column (or a window's smallest resize amount).
-- Copyright   :  (c) 2008 Justin Bogner <mail@justinbogner.com>
-- License     :  BSD3-style (as xmonad)
--
-- Maintainer  :  Justin Bogner <mail@justinbogner.com>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A layout much like Tall, but using a multiple of a window's minimum
-- resize amount instead of a percentage of screen to decide where to
-- split. This is useful when you usually leave a text editor or
-- terminal in the master pane and like it to be 80 columns wide.
--
-----------------------------------------------------------------------------

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

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

-- $usage
-- You can use this module with the following in your @xmonad.hs@:
--
-- > import XMonad.Layout.FixedColumn
--
-- Then edit your @layoutHook@ by adding the FixedColumn layout:
--
-- > myLayout = FixedColumn 1 20 80 10 ||| Full ||| etc..
-- > main = xmonad def { layoutHook = myLayout }
--
-- 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".

-- | A tiling mode based on preserving a nice fixed width
--   window. Supports 'Shrink', 'Expand' and 'IncMasterN'.
data FixedColumn a = FixedColumn !Int -- Number of windows in the master pane
                                 !Int -- Number to increment by when resizing
                                 !Int -- Default width of master pane
                                 !Int -- Column width for normal windows
                        deriving (ReadPrec [FixedColumn a]
ReadPrec (FixedColumn a)
ReadS [FixedColumn a]
forall a. ReadPrec [FixedColumn a]
forall a. ReadPrec (FixedColumn a)
forall a. Int -> ReadS (FixedColumn a)
forall a. ReadS [FixedColumn a]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [FixedColumn a]
$creadListPrec :: forall a. ReadPrec [FixedColumn a]
readPrec :: ReadPrec (FixedColumn a)
$creadPrec :: forall a. ReadPrec (FixedColumn a)
readList :: ReadS [FixedColumn a]
$creadList :: forall a. ReadS [FixedColumn a]
readsPrec :: Int -> ReadS (FixedColumn a)
$creadsPrec :: forall a. Int -> ReadS (FixedColumn a)
Read, Int -> FixedColumn a -> ShowS
forall a. Int -> FixedColumn a -> ShowS
forall a. [FixedColumn a] -> ShowS
forall a. FixedColumn a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FixedColumn a] -> ShowS
$cshowList :: forall a. [FixedColumn a] -> ShowS
show :: FixedColumn a -> String
$cshow :: forall a. FixedColumn a -> String
showsPrec :: Int -> FixedColumn a -> ShowS
$cshowsPrec :: forall a. Int -> FixedColumn a -> ShowS
Show)

instance LayoutClass FixedColumn Window where
    doLayout :: FixedColumn Window
-> Rectangle
-> Stack Window
-> X ([(Window, Rectangle)], Maybe (FixedColumn Window))
doLayout (FixedColumn Int
nmaster Int
_ Int
ncol Int
fallback) Rectangle
r Stack Window
s = do
            [Int]
fws <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (Int -> Int -> Window -> X Int
widthCols Int
fallback Int
ncol) [Window]
ws
            let frac :: Rational
frac = forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum (forall a. Int -> [a] -> [a]
take Int
nmaster [Int]
fws) forall {a} {a} {a}.
(Fractional a, Integral a, Integral a) =>
a -> a -> a
// Rectangle -> Dimension
rect_width Rectangle
r
                rs :: [Rectangle]
rs   = Rational -> Rectangle -> Int -> Int -> [Rectangle]
tile Rational
frac Rectangle
r Int
nmaster (forall (t :: * -> *) a. Foldable t => t a -> Int
length [Window]
ws)
            forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. [a] -> [b] -> [(a, b)]
zip [Window]
ws [Rectangle]
rs, forall a. Maybe a
Nothing)
        where ws :: [Window]
ws     = forall a. Stack a -> [a]
W.integrate Stack Window
s
              a
x // :: a -> a -> a
// a
y = forall a b. (Integral a, Num b) => a -> b
fromIntegral a
x forall a. Fractional a => a -> a -> a
/ forall a b. (Integral a, Num b) => a -> b
fromIntegral a
y

    pureMessage :: FixedColumn Window -> SomeMessage -> Maybe (FixedColumn Window)
pureMessage (FixedColumn Int
nmaster Int
delta Int
ncol Int
fallback) SomeMessage
m =
            forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum [forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall {a}. Resize -> FixedColumn a
resize     (forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
m)
                 ,forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall {a}. IncMasterN -> FixedColumn a
incmastern (forall m. Message m => SomeMessage -> Maybe m
fromMessage SomeMessage
m)]
        where resize :: Resize -> FixedColumn a
resize Resize
Shrink
                  = forall a. Int -> Int -> Int -> Int -> FixedColumn a
FixedColumn Int
nmaster Int
delta (forall a. Ord a => a -> a -> a
max Int
0 forall a b. (a -> b) -> a -> b
$ Int
ncol forall a. Num a => a -> a -> a
- Int
delta) Int
fallback
              resize Resize
Expand
                  = forall a. Int -> Int -> Int -> Int -> FixedColumn a
FixedColumn Int
nmaster Int
delta (Int
ncol forall a. Num a => a -> a -> a
+ Int
delta) Int
fallback
              incmastern :: IncMasterN -> FixedColumn a
incmastern (IncMasterN Int
d)
                  = forall a. Int -> Int -> Int -> Int -> FixedColumn a
FixedColumn (forall a. Ord a => a -> a -> a
max Int
0 (Int
nmasterforall a. Num a => a -> a -> a
+Int
d)) Int
delta Int
ncol Int
fallback

    description :: FixedColumn Window -> String
description FixedColumn Window
_ = String
"FixedColumn"

-- | Determine the width of @w@ given that we would like it to be @n@
--   columns wide, using @inc@ as a resize increment for windows that
--   don't have one
widthCols :: Int -> Int -> Window -> X Int
widthCols :: Int -> Int -> Window -> X Int
widthCols Int
inc Int
n Window
w = do
    Display
d  <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks XConf -> Display
display
    Int
bw <- forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fi forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (l :: * -> *). XConfig l -> Dimension
borderWidth forall b c a. (b -> c) -> (a -> b) -> a -> c
. XConf -> XConfig Layout
config
    SizeHints
sh <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
io forall a b. (a -> b) -> a -> b
$ Display -> Window -> IO SizeHints
getWMNormalHints Display
d Window
w
    let widthHint :: (SizeHints -> f (b, b)) -> f b
widthHint SizeHints -> f (b, b)
f = SizeHints -> f (b, b)
f SizeHints
sh forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst
        oneCol :: Int
oneCol      = forall a. a -> Maybe a -> a
fromMaybe Int
inc forall a b. (a -> b) -> a -> b
$ forall {f :: * -> *} {b} {b} {b}.
(Functor f, Integral b, Num b) =>
(SizeHints -> f (b, b)) -> f b
widthHint SizeHints -> Maybe (Dimension, Dimension)
sh_resize_inc
        base :: Int
base        = forall a. a -> Maybe a -> a
fromMaybe Int
0 forall a b. (a -> b) -> a -> b
$ forall {f :: * -> *} {b} {b} {b}.
(Functor f, Integral b, Num b) =>
(SizeHints -> f (b, b)) -> f b
widthHint SizeHints -> Maybe (Dimension, Dimension)
sh_base_size
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int
2 forall a. Num a => a -> a -> a
* Int
bw forall a. Num a => a -> a -> a
+ Int
base forall a. Num a => a -> a -> a
+ Int
n forall a. Num a => a -> a -> a
* Int
oneCol