{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-} ----------------------------------------------------------------------------- -- | -- Module : XMonad.Layout.MultiColumns -- Copyright : (c) Anders Engstrom -- License : BSD3-style (see LICENSE) -- -- Maintainer : Anders Engstrom -- 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