{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable #-}
module XMonad.Layout.BinarySpacePartition (
emptyBSP
, BinarySpacePartition
, Rotate(..)
, Swap(..)
, ResizeDirectional(..)
, TreeRotate(..)
, TreeBalance(..)
, FocusParent(..)
, SelectMoveNode(..)
, Direction2D(..)
) where
import XMonad
import qualified XMonad.StackSet as W
import XMonad.Util.Stack hiding (Zipper)
import XMonad.Util.Types
import XMonad.Layout.WindowArranger (WindowArrangerMsg(SetGeometry))
import XMonad.Util.XUtils
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List ((\\), elemIndex, foldl')
import Data.Maybe (fromMaybe, isNothing, isJust, mapMaybe, catMaybes)
import Control.Applicative
import Control.Monad
import Data.Ratio ((%))
data TreeRotate = RotateL | RotateR deriving Typeable
instance Message TreeRotate
data TreeBalance = Balance | Equalize deriving Typeable
instance Message TreeBalance
data ResizeDirectional = ExpandTowards Direction2D | ShrinkFrom Direction2D | MoveSplit Direction2D deriving Typeable
instance Message ResizeDirectional
data Rotate = Rotate deriving Typeable
instance Message Rotate
data Swap = Swap deriving Typeable
instance Message Swap
data FocusParent = FocusParent deriving Typeable
instance Message FocusParent
data SelectMoveNode = SelectNode | MoveNode deriving Typeable
instance Message SelectMoveNode
data Axis = Horizontal | Vertical deriving (Show, Read, Eq)
oppositeDirection :: Direction2D -> Direction2D
oppositeDirection U = D
oppositeDirection D = U
oppositeDirection L = R
oppositeDirection R = L
oppositeAxis :: Axis -> Axis
oppositeAxis Vertical = Horizontal
oppositeAxis Horizontal = Vertical
toAxis :: Direction2D -> Axis
toAxis U = Horizontal
toAxis D = Horizontal
toAxis L = Vertical
toAxis R = Vertical
split :: Axis -> Rational -> Rectangle -> (Rectangle, Rectangle)
split Horizontal r (Rectangle sx sy sw sh) = (r1, r2) where
r1 = Rectangle sx sy sw sh'
r2 = Rectangle sx (sy + fromIntegral sh') sw (sh - sh')
sh' = floor $ fromIntegral sh * r
split Vertical r (Rectangle sx sy sw sh) = (r1, r2) where
r1 = Rectangle sx sy sw' sh
r2 = Rectangle (sx + fromIntegral sw') sy (sw - sw') sh
sw' = floor $ fromIntegral sw * r
data Split = Split { axis :: Axis
, ratio :: Rational
} deriving (Show, Read, Eq)
oppositeSplit :: Split -> Split
oppositeSplit (Split d r) = Split (oppositeAxis d) r
increaseRatio :: Split -> Rational -> Split
increaseRatio (Split d r) delta = Split d (min 0.9 (max 0.1 (r + delta)))
resizeDiff :: Rational
resizeDiff = 0.05
data Tree a = Leaf Int | Node { value :: a
, left :: Tree a
, right :: Tree a
} deriving (Show, Read, Eq)
numLeaves :: Tree a -> Int
numLeaves (Leaf _) = 1
numLeaves (Node _ l r) = numLeaves l + numLeaves r
rotTree :: Direction2D -> Tree a -> Tree a
rotTree _ (Leaf n) = Leaf n
rotTree R n@(Node _ (Leaf _) _) = n
rotTree L n@(Node _ _ (Leaf _)) = n
rotTree R (Node sp (Node sp2 l2 r2) r) = Node sp2 l2 (Node sp r2 r)
rotTree L (Node sp l (Node sp2 l2 r2)) = Node sp2 (Node sp l l2) r2
rotTree _ t = t
data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show, Read, Eq)
swapCrumb :: Crumb a -> Crumb a
swapCrumb (LeftCrumb s t) = RightCrumb s t
swapCrumb (RightCrumb s t) = LeftCrumb s t
parentVal :: Crumb a -> a
parentVal (LeftCrumb s _) = s
parentVal (RightCrumb s _) = s
modifyParentVal :: (a -> a) -> Crumb a -> Crumb a
modifyParentVal f (LeftCrumb s t) = LeftCrumb (f s) t
modifyParentVal f (RightCrumb s t) = RightCrumb (f s) t
type Zipper a = (Tree a, [Crumb a])
toZipper :: Tree a -> Zipper a
toZipper t = (t, [])
goLeft :: Zipper a -> Maybe (Zipper a)
goLeft (Leaf _, _) = Nothing
goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs)
goRight :: Zipper a -> Maybe (Zipper a)
goRight (Leaf _, _) = Nothing
goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs)
goUp :: Zipper a -> Maybe (Zipper a)
goUp (_, []) = Nothing
goUp (t, LeftCrumb x r:cs) = Just (Node x t r, cs)
goUp (t, RightCrumb x l:cs) = Just (Node x l t, cs)
goSibling :: Zipper a -> Maybe (Zipper a)
goSibling (_, []) = Nothing
goSibling z@(_, LeftCrumb _ _:_) = Just z >>= goUp >>= goRight
goSibling z@(_, RightCrumb _ _:_) = Just z >>= goUp >>= goLeft
top :: Zipper a -> Zipper a
top z = case goUp z of
Nothing -> z
Just z' -> top z'
toTree :: Zipper a -> Tree a
toTree = fst . top
goToNthLeaf :: Int -> Zipper a -> Maybe (Zipper a)
goToNthLeaf _ z@(Leaf _, _) = Just z
goToNthLeaf n z@(t, _) =
if numLeaves (left t) > n
then do z' <- goLeft z
goToNthLeaf n z'
else do z' <- goRight z
goToNthLeaf (n - (numLeaves . left $ t)) z'
toggleSplits :: Tree Split -> Tree Split
toggleSplits (Leaf l) = Leaf l
toggleSplits (Node s l r) = Node (oppositeSplit s) (toggleSplits l) (toggleSplits r)
splitCurrent :: Zipper Split -> Maybe (Zipper Split)
splitCurrent (Leaf _, []) = Just (Node (Split Vertical 0.5) (Leaf 0) (Leaf 0), [])
splitCurrent (Leaf _, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf 0) (Leaf 0), crumb:cs)
splitCurrent (n, []) = Just (Node (Split Vertical 0.5) (Leaf 0) (toggleSplits n), [])
splitCurrent (n, crumb:cs) = Just (Node (Split (oppositeAxis . axis . parentVal $ crumb) 0.5) (Leaf 0) (toggleSplits n), crumb:cs)
removeCurrent :: Zipper a -> Maybe (Zipper a)
removeCurrent (Leaf _, LeftCrumb _ r:cs) = Just (r, cs)
removeCurrent (Leaf _, RightCrumb _ l:cs) = Just (l, cs)
removeCurrent (Leaf _, []) = Nothing
removeCurrent (Node _ (Leaf _) r@(Node _ _ _), cs) = Just (r, cs)
removeCurrent (Node _ l@(Node _ _ _) (Leaf _), cs) = Just (l, cs)
removeCurrent (Node _ (Leaf _) (Leaf _), cs) = Just (Leaf 0, cs)
removeCurrent z@(Node _ _ _, _) = goLeft z >>= removeCurrent
rotateCurrent :: Zipper Split -> Maybe (Zipper Split)
rotateCurrent l@(_, []) = Just l
rotateCurrent (n, c:cs) = Just (n, modifyParentVal oppositeSplit c:cs)
swapCurrent :: Zipper a -> Maybe (Zipper a)
swapCurrent l@(_, []) = Just l
swapCurrent (n, c:cs) = Just (n, swapCrumb c:cs)
isAllTheWay :: Direction2D -> Zipper Split -> Bool
isAllTheWay _ (_, []) = True
isAllTheWay R (_, LeftCrumb s _:_)
| axis s == Vertical = False
isAllTheWay L (_, RightCrumb s _:_)
| axis s == Vertical = False
isAllTheWay D (_, LeftCrumb s _:_)
| axis s == Horizontal = False
isAllTheWay U (_, RightCrumb s _:_)
| axis s == Horizontal = False
isAllTheWay dir z = fromMaybe False $ goUp z >>= Just . isAllTheWay dir
expandTreeTowards :: Direction2D -> Zipper Split -> Maybe (Zipper Split)
expandTreeTowards _ z@(_, []) = Just z
expandTreeTowards dir z
| isAllTheWay dir z = shrinkTreeFrom (oppositeDirection dir) z
expandTreeTowards R (t, LeftCrumb s r:cs)
| axis s == Vertical = Just (t, LeftCrumb (increaseRatio s resizeDiff) r:cs)
expandTreeTowards L (t, RightCrumb s l:cs)
| axis s == Vertical = Just (t, RightCrumb (increaseRatio s (-resizeDiff)) l:cs)
expandTreeTowards D (t, LeftCrumb s r:cs)
| axis s == Horizontal = Just (t, LeftCrumb (increaseRatio s resizeDiff) r:cs)
expandTreeTowards U (t, RightCrumb s l:cs)
| axis s == Horizontal = Just (t, RightCrumb (increaseRatio s (-resizeDiff)) l:cs)
expandTreeTowards dir z = goUp z >>= expandTreeTowards dir
shrinkTreeFrom :: Direction2D -> Zipper Split -> Maybe (Zipper Split)
shrinkTreeFrom _ z@(_, []) = Just z
shrinkTreeFrom R z@(_, LeftCrumb s _:_)
| axis s == Vertical = Just z >>= goSibling >>= expandTreeTowards L
shrinkTreeFrom L z@(_, RightCrumb s _:_)
| axis s == Vertical = Just z >>= goSibling >>= expandTreeTowards R
shrinkTreeFrom D z@(_, LeftCrumb s _:_)
| axis s == Horizontal = Just z >>= goSibling >>= expandTreeTowards U
shrinkTreeFrom U z@(_, RightCrumb s _:_)
| axis s == Horizontal = Just z >>= goSibling >>= expandTreeTowards D
shrinkTreeFrom dir z = goUp z >>= shrinkTreeFrom dir
autoSizeTree :: Direction2D -> Zipper Split -> Maybe (Zipper Split)
autoSizeTree _ z@(_, []) = Just z
autoSizeTree d z =
Just z >>= getSplit (toAxis d) >>= resizeTree d
resizeTree :: Direction2D -> Zipper Split -> Maybe (Zipper Split)
resizeTree _ z@(_, []) = Just z
resizeTree R z@(_, LeftCrumb _ _:_) =
Just z >>= expandTreeTowards R
resizeTree L z@(_, LeftCrumb _ _:_) =
Just z >>= shrinkTreeFrom R
resizeTree U z@(_, LeftCrumb _ _:_) =
Just z >>= shrinkTreeFrom D
resizeTree D z@(_, LeftCrumb _ _:_) =
Just z >>= expandTreeTowards D
resizeTree R z@(_, RightCrumb _ _:_) =
Just z >>= shrinkTreeFrom L
resizeTree L z@(_, RightCrumb _ _:_) =
Just z >>= expandTreeTowards L
resizeTree U z@(_, RightCrumb _ _:_) =
Just z >>= expandTreeTowards U
resizeTree D z@(_, RightCrumb _ _:_) =
Just z >>= shrinkTreeFrom U
getSplit :: Axis -> Zipper Split -> Maybe (Zipper Split)
getSplit _ (_, []) = Nothing
getSplit d z =
do let fs = findSplit d z
if isNothing fs
then findClosest d z
else fs
findClosest :: Axis -> Zipper Split -> Maybe (Zipper Split)
findClosest _ z@(_, []) = Just z
findClosest d z@(_, LeftCrumb s _:_)
| axis s == d = Just z
findClosest d z@(_, RightCrumb s _:_)
| axis s == d = Just z
findClosest d z = goUp z >>= findClosest d
findSplit :: Axis -> Zipper Split -> Maybe (Zipper Split)
findSplit _ (_, []) = Nothing
findSplit d z@(_, LeftCrumb s _:_)
| axis s == d = Just z
findSplit d z = goUp z >>= findSplit d
resizeSplit :: Direction2D -> (Rational,Rational) -> Zipper Split -> Maybe (Zipper Split)
resizeSplit _ _ z@(_, []) = Just z
resizeSplit dir (xsc,ysc) z = case goToBorder dir z of
Nothing -> Just z
Just (t, crumb) -> Just $ case dir of
R -> (t{value=sp{ratio=scaleRatio (ratio sp) xsc}}, crumb)
D -> (t{value=sp{ratio=scaleRatio (ratio sp) ysc}}, crumb)
L -> (t{value=sp{ratio=1-scaleRatio (1-ratio sp) xsc}}, crumb)
U -> (t{value=sp{ratio=1-scaleRatio (1-ratio sp) ysc}}, crumb)
where sp = value t
scaleRatio r fac = min 0.9 $ max 0.1 $ r*fac
goToBorder :: Direction2D -> Zipper Split -> Maybe (Zipper Split)
goToBorder L z@(_, RightCrumb (Split Vertical _) _:_) = goUp z
goToBorder L z = goUp z >>= goToBorder L
goToBorder R z@(_, LeftCrumb (Split Vertical _) _:_) = goUp z
goToBorder R z = goUp z >>= goToBorder R
goToBorder U z@(_, RightCrumb (Split Horizontal _) _:_) = goUp z
goToBorder U z = goUp z >>= goToBorder U
goToBorder D z@(_, LeftCrumb (Split Horizontal _) _:_) = goUp z
goToBorder D z = goUp z >>= goToBorder D
numerate :: [Int] -> Tree a -> Tree a
numerate ns t = snd $ num ns t
where num (n:nns) (Leaf _) = (nns, Leaf n)
num [] (Leaf _) = ([], Leaf 0)
num n (Node s l r) = (n'', Node s nl nr)
where (n', nl) = num n l
(n'', nr) = num n' r
flatten :: Tree a -> [Int]
flatten (Leaf n) = [n]
flatten (Node _ l r) = flatten l++flatten r
equalize :: Zipper Split -> Maybe (Zipper Split)
equalize (t, cs) = Just (eql t, cs)
where eql (Leaf n) = Leaf n
eql n@(Node s l r) = Node s{ratio=fromIntegral (numLeaves l) % fromIntegral (numLeaves n)}
(eql l) (eql r)
balancedTree :: Zipper Split -> Maybe (Zipper Split)
balancedTree (t, cs) = Just (numerate (flatten t) $ balanced (numLeaves t), cs)
where balanced 1 = Leaf 0
balanced 2 = Node (Split Horizontal 0.5) (Leaf 0) (Leaf 0)
balanced m = Node (Split Horizontal 0.5) (balanced (m`div`2)) (balanced (m-m`div`2))
optimizeOrientation :: Rectangle -> Zipper Split -> Maybe (Zipper Split)
optimizeOrientation rct (t, cs) = Just (opt t rct, cs)
where opt (Leaf v) _ = Leaf v
opt (Node sp l r) rect = Node sp' (opt l lrect) (opt r rrect)
where (Rectangle _ _ w1 h1,Rectangle _ _ w2 h2) = split (axis sp) (ratio sp) rect
(Rectangle _ _ w3 h3,Rectangle _ _ w4 h4) = split (axis $ oppositeSplit sp) (ratio sp) rect
f w h = if w > h then w'/h' else h'/w' where (w',h') = (fromIntegral w :: Double, fromIntegral h :: Double)
wratio = min (f w1 h1) (f w2 h2)
wratio' = min (f w3 h3) (f w4 h4)
sp' = if wratio<wratio' then sp else oppositeSplit sp
(lrect, rrect) = split (axis sp') (ratio sp') rect
data NodeRef = NodeRef { refLeaf :: Int, refPath :: [Direction2D], refWins :: [Window] } deriving (Show,Read,Eq)
noRef :: NodeRef
noRef = NodeRef (-1) [] []
goToNode :: NodeRef -> Zipper a -> Maybe (Zipper a)
goToNode (NodeRef _ dirs _) z = foldM gofun z dirs
where gofun z' L = goLeft z'
gofun z' R = goRight z'
gofun _ _ = Nothing
toNodeRef :: Int -> Maybe (Zipper Split) -> NodeRef
toNodeRef _ Nothing = noRef
toNodeRef l (Just (_, cs)) = NodeRef l (reverse $ map crumbToDir cs) []
where crumbToDir (LeftCrumb _ _) = L
crumbToDir (RightCrumb _ _) = R
nodeRefToLeaf :: NodeRef -> Maybe (Zipper a) -> Maybe Int
nodeRefToLeaf n (Just z) = case goToNode n z of
Just (Leaf l, _) -> Just l
Just (Node _ _ _, _) -> Nothing
Nothing -> Nothing
nodeRefToLeaf _ Nothing = Nothing
leafToNodeRef :: Int -> BinarySpacePartition a -> NodeRef
leafToNodeRef l b = toNodeRef l (makeZipper b >>= goToNthLeaf l)
data BinarySpacePartition a = BinarySpacePartition { getOldRects :: [(Window,Rectangle)]
, getFocusedNode :: NodeRef
, getSelectedNode :: NodeRef
, getTree :: Maybe (Tree Split) } deriving (Show, Read,Eq)
emptyBSP :: BinarySpacePartition a
emptyBSP = BinarySpacePartition [] noRef noRef Nothing
makeBSP :: Tree Split -> BinarySpacePartition a
makeBSP = BinarySpacePartition [] noRef noRef . Just
makeZipper :: BinarySpacePartition a -> Maybe (Zipper Split)
makeZipper (BinarySpacePartition _ _ _ Nothing) = Nothing
makeZipper (BinarySpacePartition _ _ _ (Just t)) = Just . toZipper $ t
size :: BinarySpacePartition a -> Int
size = maybe 0 numLeaves . getTree
zipperToBinarySpacePartition :: Maybe (Zipper Split) -> BinarySpacePartition b
zipperToBinarySpacePartition Nothing = emptyBSP
zipperToBinarySpacePartition (Just z) = BinarySpacePartition [] noRef noRef . Just . toTree . top $ z
rectangles :: BinarySpacePartition a -> Rectangle -> [Rectangle]
rectangles (BinarySpacePartition _ _ _ Nothing) _ = []
rectangles (BinarySpacePartition _ _ _ (Just (Leaf _))) rootRect = [rootRect]
rectangles (BinarySpacePartition _ _ _ (Just node)) rootRect =
rectangles (makeBSP . left $ node) leftBox ++
rectangles (makeBSP . right $ node) rightBox
where (leftBox, rightBox) = split (axis info) (ratio info) rootRect
info = value node
getNodeRect :: BinarySpacePartition a -> Rectangle -> NodeRef -> Rectangle
getNodeRect b r n = fromMaybe (Rectangle 0 0 1 1) (makeZipper b >>= goToNode n >>= getRect [])
where getRect ls (_, []) = Just $ foldl (\r' (s,f) -> f $ split' s r') r ls
getRect ls z@(_, LeftCrumb s _:_) = goUp z >>= getRect ((s,fst):ls)
getRect ls z@(_, RightCrumb s _:_) = goUp z >>= getRect ((s,snd):ls)
split' s = split (axis s) (ratio s)
doToNth :: (Zipper Split -> Maybe (Zipper Split)) -> BinarySpacePartition a -> BinarySpacePartition a
doToNth f b = b{getTree=getTree $ zipperToBinarySpacePartition $ makeZipper b >>= goToNode (getFocusedNode b) >>= f}
splitNth :: BinarySpacePartition a -> BinarySpacePartition a
splitNth (BinarySpacePartition _ _ _ Nothing) = makeBSP (Leaf 0)
splitNth b = doToNth splitCurrent b
removeNth :: BinarySpacePartition a -> BinarySpacePartition a
removeNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
removeNth (BinarySpacePartition _ _ _ (Just (Leaf _))) = emptyBSP
removeNth b = doToNth removeCurrent b
rotateNth :: BinarySpacePartition a -> BinarySpacePartition a
rotateNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
rotateNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
rotateNth b = doToNth rotateCurrent b
swapNth :: BinarySpacePartition a -> BinarySpacePartition a
swapNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
swapNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
swapNth b = doToNth swapCurrent b
growNthTowards :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
growNthTowards _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
growNthTowards _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
growNthTowards dir b = doToNth (expandTreeTowards dir) b
shrinkNthFrom :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
shrinkNthFrom _ (BinarySpacePartition _ _ _ Nothing)= emptyBSP
shrinkNthFrom _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
shrinkNthFrom dir b = doToNth (shrinkTreeFrom dir) b
autoSizeNth :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
autoSizeNth _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
autoSizeNth _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
autoSizeNth dir b = doToNth (autoSizeTree dir) b
resizeSplitNth :: Direction2D -> (Rational,Rational) -> BinarySpacePartition a -> BinarySpacePartition a
resizeSplitNth _ _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
resizeSplitNth _ _ b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
resizeSplitNth dir sc b = doToNth (resizeSplit dir sc) b
rotateTreeNth :: Direction2D -> BinarySpacePartition a -> BinarySpacePartition a
rotateTreeNth _ (BinarySpacePartition _ _ _ Nothing) = emptyBSP
rotateTreeNth U b = b
rotateTreeNth D b = b
rotateTreeNth dir b@(BinarySpacePartition _ _ _ (Just _)) =
doToNth (\t -> case goUp t of
Nothing -> Just t
Just (t', c) -> Just (rotTree dir t', c)) b
equalizeNth :: BinarySpacePartition a -> BinarySpacePartition a
equalizeNth (BinarySpacePartition _ _ _ Nothing) = emptyBSP
equalizeNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) = b
equalizeNth b = doToNth equalize b
rebalanceNth :: BinarySpacePartition a -> Rectangle -> BinarySpacePartition a
rebalanceNth (BinarySpacePartition _ _ _ Nothing) _ = emptyBSP
rebalanceNth b@(BinarySpacePartition _ _ _ (Just (Leaf _))) _ = b
rebalanceNth b r = doToNth (balancedTree >=> optimizeOrientation r) b
flattenLeaves :: BinarySpacePartition a -> [Int]
flattenLeaves (BinarySpacePartition _ _ _ Nothing) = []
flattenLeaves (BinarySpacePartition _ _ _ (Just t)) = flatten t
numerateLeaves :: BinarySpacePartition a -> BinarySpacePartition a
numerateLeaves b@(BinarySpacePartition _ _ _ Nothing) = b
numerateLeaves b@(BinarySpacePartition _ _ _ (Just t)) = b{getTree=Just $ numerate ns t}
where ns = [0..(numLeaves t-1)]
moveNode :: BinarySpacePartition a -> BinarySpacePartition a
moveNode b@(BinarySpacePartition _ (NodeRef (-1) _ _) _ _) = b
moveNode b@(BinarySpacePartition _ _ (NodeRef (-1) _ _) _) = b
moveNode b@(BinarySpacePartition _ _ _ Nothing) = b
moveNode b@(BinarySpacePartition _ f s (Just ot)) =
case makeZipper b >>= goToNode s of
Just (n, LeftCrumb _ t:cs) -> b{getTree=Just $ insert n $ top (t, cs)}
Just (n, RightCrumb _ t:cs) -> b{getTree=Just $ insert n $ top (t, cs)}
_ -> b
where insert t z = case goToNode f z of
Nothing -> ot
Just (n, c:cs) -> toTree (Node (Split (oppositeAxis . axis . parentVal $ c) 0.5) t n, c:cs)
Just (n, []) -> toTree (Node (Split Vertical 0.5) t n, [])
index :: W.Stack a -> Int
index s = case toIndex (Just s) of
(_, Nothing) -> 0
(_, Just int) -> int
adjustStack :: Maybe (W.Stack Window)
-> Maybe (W.Stack Window)
-> [Window]
-> Maybe (BinarySpacePartition Window)
-> Maybe (W.Stack Window)
adjustStack orig Nothing _ _ = orig
adjustStack orig _ _ Nothing = orig
adjustStack orig s fw (Just b) =
if length ls<length ws then orig
else fromIndex ws' fid'
where ws' = mapMaybe (`M.lookup` wsmap) ls ++ fw
fid' = fromMaybe 0 $ elemIndex focused ws'
wsmap = M.fromList $ zip [0..] ws
ls = flattenLeaves b
(ws,fid) = toIndex s
focused = ws !! fromMaybe 0 fid
replaceStack :: Maybe (W.Stack Window) -> X ()
replaceStack s = do
st <- get
let wset = windowset st
cur = W.current wset
wsp = W.workspace cur
put st{windowset=wset{W.current=cur{W.workspace=wsp{W.stack=s}}}}
replaceFloating :: M.Map Window W.RationalRect -> X ()
replaceFloating wsm = do
st <- get
let wset = windowset st
put st{windowset=wset{W.floating=wsm}}
getFloating :: X [Window]
getFloating = (M.keys . W.floating) <$> gets windowset
getStackSet :: X (Maybe (W.Stack Window))
getStackSet = (W.stack . W.workspace . W.current) <$> gets windowset
getScreenRect :: X Rectangle
getScreenRect = (screenRect . W.screenDetail . W.current) <$> gets windowset
withoutFloating :: [Window] -> Maybe (W.Stack Window) -> Maybe (W.Stack Window)
withoutFloating fs = maybe Nothing (unfloat fs)
unfloat :: [Window] -> W.Stack Window -> Maybe (W.Stack Window)
unfloat fs s = if W.focus s `elem` fs
then Nothing
else Just $ s{W.up = W.up s \\ fs, W.down = W.down s \\ fs}
instance LayoutClass BinarySpacePartition Window where
doLayout b r s = do
let b' = layout b
b'' <- updateNodeRef b' (size b/=size b') r
let rs = rectangles b'' r
wrs = zip ws rs
return (wrs, Just b''{getOldRects=wrs})
where
ws = W.integrate s
l = length ws
layout bsp
| l == sz = bsp
| l > sz = layout $ splitNth bsp
| otherwise = layout $ removeNth bsp
where sz = size bsp
handleMessage b_orig m
| Just msg@(SetGeometry _) <- fromMessage m = handleResize b msg
| Just FocusParent <- fromMessage m = do
let n = getFocusedNode b
let n' = toNodeRef (refLeaf n) (makeZipper b >>= goToNode n >>= goUp)
return $ Just b{getFocusedNode=n'{refWins=refWins n}}
| Just SelectNode <- fromMessage m = do
let n = getFocusedNode b
let s = getSelectedNode b
removeBorder $ refWins s
let s' = if refLeaf n == refLeaf s && refPath n == refPath s
then noRef else n{refWins=[]}
return $ Just b{getSelectedNode=s'}
| otherwise = do
ws <- getStackSet
fs <- getFloating
r <- getScreenRect
let lws = withoutFloating fs ws
lfs = maybe [] W.integrate ws \\ maybe [] W.integrate lws
b' = handleMesg r
ws' = adjustStack ws lws lfs b'
replaceStack ws'
return b'
where handleMesg r = msum [ fmap resize (fromMessage m)
, fmap rotate (fromMessage m)
, fmap swap (fromMessage m)
, fmap rotateTr (fromMessage m)
, fmap (balanceTr r) (fromMessage m)
, fmap move (fromMessage m)
]
resize (ExpandTowards dir) = growNthTowards dir b
resize (ShrinkFrom dir) = shrinkNthFrom dir b
resize (MoveSplit dir) = autoSizeNth dir b
rotate Rotate = resetFoc $ rotateNth b
swap Swap = resetFoc $ swapNth b
rotateTr RotateL = resetFoc $ rotateTreeNth L b
rotateTr RotateR = resetFoc $ rotateTreeNth R b
balanceTr _ Equalize = resetFoc $ equalizeNth b
balanceTr r Balance = resetFoc $ rebalanceNth b r
move MoveNode = resetFoc $ moveNode b
move SelectNode = b
b = numerateLeaves b_orig
resetFoc bsp = bsp{getFocusedNode=(getFocusedNode bsp){refLeaf=(-1)}
,getSelectedNode=(getSelectedNode bsp){refLeaf=(-1)}}
description _ = "BSP"
handleResize :: BinarySpacePartition Window -> WindowArrangerMsg -> X (Maybe (BinarySpacePartition Window))
handleResize b (SetGeometry newrect@(Rectangle _ _ w h)) = do
ws <- getStackSet
fs <- getFloating
case W.focus <$> ws of
Nothing -> return Nothing
Just win -> do
(_,_,_,_,_,mx,my,_) <- withDisplay (\d -> io $ queryPointer d win)
let oldrect@(Rectangle _ _ ow oh) = fromMaybe (Rectangle 0 0 0 0) $ lookup win $ getOldRects b
let (xsc,ysc) = (fi w % fi ow, fi h % fi oh)
(xsc',ysc') = (rough xsc, rough ysc)
dirs = changedDirs oldrect newrect (fi mx,fi my)
n = elemIndex win $ maybe [] W.integrate $ withoutFloating fs ws
return $ case n of
Just _ -> Just $ foldl' (\b' d -> resizeSplitNth d (xsc',ysc') b') b dirs
Nothing -> Nothing
where rough v = min 1.5 $ max 0.75 v
handleResize _ _ = return Nothing
changedDirs :: Rectangle -> Rectangle -> (Int,Int) -> [Direction2D]
changedDirs (Rectangle _ _ ow oh) (Rectangle _ _ w h) (mx,my) = catMaybes [lr, ud]
where lr = if ow==w then Nothing
else Just (if (fi mx :: Double) > (fi ow :: Double)/2 then R else L)
ud = if oh==h then Nothing
else Just (if (fi my :: Double) > (fi oh :: Double)/2 then D else U)
updateNodeRef :: BinarySpacePartition Window -> Bool -> Rectangle -> X (BinarySpacePartition Window)
updateNodeRef b force r = do
let n = getFocusedNode b
let s = getSelectedNode b
removeBorder (refWins n++refWins s)
l <- getCurrFocused
b' <- if refLeaf n /= l || refLeaf n == (-1) || force
then return b{getFocusedNode=leafToNodeRef l b}
else return b
b'' <- if force then return b'{getSelectedNode=noRef} else return b'
renderBorders r b''
where getCurrFocused = maybe 0 index <$> (withoutFloating <$> getFloating <*> getStackSet)
renderBorders :: Rectangle -> BinarySpacePartition a -> X (BinarySpacePartition a)
renderBorders r b = do
let l = nodeRefToLeaf (getFocusedNode b) $ makeZipper b
wssel <- if refLeaf (getSelectedNode b)/=(-1)
then createBorder (getNodeRect b r (getSelectedNode b)) $ Just "#00ff00"
else return []
let b' = b{getSelectedNode=(getSelectedNode b){refWins=wssel}}
if refLeaf (getFocusedNode b')==(-1) || isJust l || size b'<2 then return b'
else do
ws' <- createBorder (getNodeRect b' r (getFocusedNode b')) Nothing
return b'{getFocusedNode=(getFocusedNode b'){refWins=ws'}}
createBorder :: Rectangle -> Maybe String -> X [Window]
createBorder (Rectangle wx wy ww wh) c = do
bw <- asks (borderWidth.config)
bc <- case c of
Nothing -> asks (focusedBorderColor.config)
Just s -> return s
let rects = [ Rectangle wx wy ww (fi bw)
, Rectangle wx wy (fi bw) wh
, Rectangle wx (wy+fi wh-fi bw) ww (fi bw)
, Rectangle (wx+fi ww-fi bw) wy (fi bw) wh
]
ws <- mapM (\r -> createNewWindow r Nothing bc False) rects
showWindows ws
maybe Nothing (\s -> Just s{W.down=W.down s ++ ws}) <$> getStackSet >>= replaceStack
M.union (M.fromList $ zip ws $ map toRR rects) . W.floating . windowset <$> get >>= replaceFloating
modify (\s -> s{mapped=mapped s `S.union` S.fromList ws})
return ws
where toRR (Rectangle x y w h) = W.RationalRect (fi x) (fi y) (fi w) (fi h)
removeBorder :: [Window] -> X ()
removeBorder ws = do
modify (\s -> s{mapped = mapped s `S.difference` S.fromList ws})
flip (foldl (flip M.delete)) ws . W.floating . windowset <$> get >>= replaceFloating
maybe Nothing (\s -> Just s{W.down=W.down s \\ ws}) <$> getStackSet >>= replaceStack
deleteWindows ws