mirror of
https://github.com/elkowar/dots-of-war.git
synced 2024-12-27 06:32:23 +00:00
184 lines
7.4 KiB
Haskell
184 lines
7.4 KiB
Haskell
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
|
||
|
|
||
|
-----------------------------------------------------------------------------
|
||
|
-- |
|
||
|
-- Module : XMonad.Layout.MultiColumns
|
||
|
-- Copyright : (c) Anders Engstrom <ankaan@gmail.com>
|
||
|
-- License : BSD3-style (see LICENSE)
|
||
|
--
|
||
|
-- Maintainer : Anders Engstrom <ankaan@gmail.com>
|
||
|
-- Stability : unstable
|
||
|
-- Portability : unportable
|
||
|
--
|
||
|
-- This layout tiles windows in a growing number of columns. The number of
|
||
|
-- windows in each column can be controlled by messages.
|
||
|
-----------------------------------------------------------------------------
|
||
|
|
||
|
module MultiColumns (
|
||
|
-- * Usage
|
||
|
-- $usage
|
||
|
|
||
|
multiCol,
|
||
|
MultiCol,
|
||
|
) where
|
||
|
|
||
|
import XMonad
|
||
|
import qualified XMonad.StackSet as W
|
||
|
|
||
|
import Control.Monad
|
||
|
|
||
|
-- $usage
|
||
|
-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
|
||
|
--
|
||
|
-- > import XMonad.Layout.MultiColumns
|
||
|
--
|
||
|
-- Then edit your @layoutHook@ by adding the multiCol layout:
|
||
|
--
|
||
|
-- > myLayouts = multiCol [1] 4 0.01 0.5 ||| etc..
|
||
|
-- > main = xmonad def { layoutHook = myLayouts }
|
||
|
--
|
||
|
-- Or alternatively:
|
||
|
--
|
||
|
-- > myLayouts = Mirror (multiCol [1] 2 0.01 (-0.25)) ||| etc..
|
||
|
-- > main = xmonad def { layoutHook = myLayouts }
|
||
|
--
|
||
|
-- The maximum number of windows in a column can be controlled using the
|
||
|
-- IncMasterN messages and the column containing the focused window will be
|
||
|
-- modified. If the value is 0, all remaining windows will be placed in that
|
||
|
-- column when all columns before that has been filled.
|
||
|
--
|
||
|
-- The size can be set to between 1 and -0.5. If the value is positive, the
|
||
|
-- master column will be of that size. The rest of the screen is split among
|
||
|
-- the other columns. But if the size is negative, it instead indicates the
|
||
|
-- size of all non-master columns and the master column will cover the rest of
|
||
|
-- the screen. If the master column would become smaller than the other
|
||
|
-- columns, the screen is instead split equally among all columns. Therefore,
|
||
|
-- if equal size among all columns are desired, set the size to -0.5.
|
||
|
--
|
||
|
-- For more detailed instructions on editing the layoutHook see:
|
||
|
--
|
||
|
-- "XMonad.Doc.Extending#Editing_the_layout_hook"
|
||
|
|
||
|
-- | Layout constructor.
|
||
|
multiCol
|
||
|
:: [Int] -- ^ Windows in each column, starting with master. Set to 0 to catch the rest.
|
||
|
-> Int -- ^ Default value for all following columns.
|
||
|
-> Rational -- ^ How much to change size each time.
|
||
|
-> MultiCol a
|
||
|
multiCol n defn ds = MultiCol (map (max 0) n) (max 0 defn) ds [0.25, 0.25, 0.25, 0.25] 0
|
||
|
|
||
|
data MultiCol a = MultiCol
|
||
|
{ multiColNWin :: ![Int]
|
||
|
, multiColDefWin :: !Int
|
||
|
, multiColDeltaSize :: !Rational
|
||
|
, multiColSize :: ![Rational]
|
||
|
, multiColActive :: !Int
|
||
|
} deriving (Show,Read,Eq)
|
||
|
|
||
|
instance LayoutClass MultiCol a where
|
||
|
doLayout l r s = return (combine s rlist, resl)
|
||
|
where rlist = doL (multiColSize l') (multiColNWin l') r wlen
|
||
|
wlen = length $ W.integrate s
|
||
|
-- Make sure the list of columns is big enough and update active column
|
||
|
nw = multiColNWin l ++ repeat (multiColDefWin l)
|
||
|
newMultiColNWin = take (max (length $ multiColNWin l) $ getCol (wlen-1) nw + 1) nw
|
||
|
newColCnt = length newMultiColNWin - length (multiColNWin l)
|
||
|
l' = l { multiColNWin = newMultiColNWin
|
||
|
, multiColActive = getCol (length $ W.up s) nw
|
||
|
, multiColSize = if newColCnt >= 0 then normalizeFractions $ multiColSize l ++ replicate newColCnt 0.5
|
||
|
else normalizeFractions $ reverse . drop (abs newColCnt) $ reverse (multiColSize l)
|
||
|
}
|
||
|
-- Only return new layout if it has been modified
|
||
|
resl = if l'==l
|
||
|
then Nothing
|
||
|
else Just l'
|
||
|
combine (W.Stack foc left right) rs = zip (foc : reverse left ++ right) $ raiseFocused (length left) rs
|
||
|
handleMessage l m =
|
||
|
return $ msum [ fmap resize (fromMessage m)
|
||
|
, fmap incmastern (fromMessage m) ]
|
||
|
where
|
||
|
resize Shrink = l { multiColSize = changeFractionAt (\x -> x - delta) activeCol (multiColSize l)}
|
||
|
resize Expand = l { multiColSize = changeFractionAt (+ delta) activeCol (multiColSize l)}
|
||
|
|
||
|
--resize Shrink = l { multiColSize = max (-0.5) $ s-ds }
|
||
|
--resize Expand = l { multiColSize = min 1 $ s+ds }
|
||
|
incmastern (IncMasterN x) = l { multiColNWin = take activeCol n ++ [newval] ++ tail r }
|
||
|
where newval = max 0 $ head r + x
|
||
|
r = drop activeCol n
|
||
|
n = multiColNWin l
|
||
|
delta = multiColDeltaSize l
|
||
|
activeCol = multiColActive l
|
||
|
description _ = "MultiCol"
|
||
|
|
||
|
raiseFocused :: Int -> [a] -> [a]
|
||
|
raiseFocused n xs = actual ++ before ++ after
|
||
|
where (before,rest) = splitAt n xs
|
||
|
(actual,after) = splitAt 1 rest
|
||
|
|
||
|
-- | Get which column a window is in, starting at 0.
|
||
|
getCol :: Int -> [Int] -> Int
|
||
|
getCol w (n:ns) = if n<1 || w < n
|
||
|
then 0
|
||
|
else 1 + getCol (w-n) ns
|
||
|
-- Should never occur...
|
||
|
getCol _ _ = -1
|
||
|
|
||
|
|
||
|
|
||
|
doL :: [Rational] -> [Int] -> Rectangle -> Int -> [Rectangle]
|
||
|
doL ratios nwin r n = rlist
|
||
|
where -- Number of columns to tile
|
||
|
ncol = getCol (n-1) nwin + 1
|
||
|
-- Compute the actual size
|
||
|
--size = floor $ abs s * fromIntegral (rect_width r)
|
||
|
-- Extract all but last column to tile
|
||
|
c = take (ncol-1) nwin
|
||
|
-- Compute number of windows in last column and add it to the others
|
||
|
col = c ++ [n-sum c]
|
||
|
-- Compute width of columns
|
||
|
--width = if s>0
|
||
|
--then if ncol==1
|
||
|
---- Only one window
|
||
|
--then [fromIntegral $ rect_width r]
|
||
|
---- Give the master it's space and split the rest equally for the other columns
|
||
|
--else size:replicate (ncol-1) ((fromIntegral (rect_width r) - size) `div` (ncol-1))
|
||
|
--else if fromIntegral ncol * abs s >= 1
|
||
|
---- Split equally
|
||
|
--then replicate ncol $ fromIntegral (rect_width r) `div` ncol
|
||
|
---- Let the master cover what is left...
|
||
|
--else (fromIntegral (rect_width r) - (ncol-1)*size):replicate (ncol-1) size
|
||
|
-- Compute the horizontal position of columns
|
||
|
xpos = accumEx (fromIntegral $ rect_x r) width
|
||
|
-- Exclusive accumulation
|
||
|
accumEx a (x:xs) = a:accumEx (a+x) xs
|
||
|
accumEx _ _ = []
|
||
|
-- Create a rectangle for each column
|
||
|
cr = zipWith (\x w -> r { rect_x=floor x, rect_width=floor w }) xpos width
|
||
|
-- Split the columns into the windows
|
||
|
rlist = concat $ zipWith splitVertically col cr
|
||
|
|
||
|
width = map (fromIntegral rw *) ratios
|
||
|
where Rectangle _ _ rw _ = r
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
normalizeFractions :: Fractional a => [a] -> [a]
|
||
|
normalizeFractions list = map (/ total) list
|
||
|
where total = sum list
|
||
|
|
||
|
|
||
|
changeFractionAt :: Fractional a => (a -> a) -> Int -> [a] -> [a]
|
||
|
changeFractionAt update idx list = normalizeFractions $ updateAt update idx (normalizeFractions list)
|
||
|
|
||
|
|
||
|
|
||
|
updateAt :: (a -> a) -> Int -> [a] -> [a]
|
||
|
updateAt _ _ [] = []
|
||
|
updateAt f 0 (x:xs) = f x : xs
|
||
|
updateAt f n (x:xs) = x : updateAt f (n - 1) xs
|