ymonad-0.1.0.0: src/YMonad/Core/StackSet.hs
module YMonad.Core.StackSet (
Stack (..),
Workspace (..),
Screen (..),
WindowSet (..),
emptyWorkspace,
singleton,
view,
shift,
focusWindow,
insertUp,
delete,
focusUp,
focusDown,
swapUp,
swapDown,
peek,
integrate,
allWindows,
modifyCurrentWorkspace,
) where
import Brolude (until)
import Data.Map.Strict qualified as M
import YMonad.Core.Common (Rect)
data Stack a = Stack
{ stackFocus :: a
, stackUp :: [a]
, stackDown :: [a]
}
deriving stock (Eq, Ord, Show)
data Workspace i l a = Workspace
{ workspaceId :: i
, workspaceLayout :: l
, workspaceStack :: Maybe (Stack a)
, workspaceRestoreLayout :: Maybe l
}
deriving stock (Eq, Ord, Show)
data Screen sid i l a = Screen
{ screenWorkspace :: Workspace i l a
, screenId :: sid
, screenRect :: Rect
}
deriving stock (Eq, Ord, Show)
data WindowSet sid i l a = WindowSet
{ current :: Screen sid i l a
, visible :: [Screen sid i l a]
, hidden :: [Workspace i l a]
, floating :: M.Map a Rect
}
deriving stock (Eq, Ord, Show)
emptyWorkspace :: i -> l -> Workspace i l a
emptyWorkspace wid lay =
Workspace
{ workspaceId = wid
, workspaceLayout = lay
, workspaceStack = Nothing
, workspaceRestoreLayout = Nothing
}
singleton :: Screen sid i l a -> [Workspace i l a] -> WindowSet sid i l a
singleton scr hs =
WindowSet
{ current = scr
, visible = []
, hidden = hs
, floating = M.empty
}
modifyCurrentWorkspace :: (Workspace i l a -> Workspace i l a) -> WindowSet sid i l a -> WindowSet sid i l a
modifyCurrentWorkspace f ws = ws {current = (current ws) {screenWorkspace = f (screenWorkspace (current ws))}}
withCurrentStack :: b -> (Stack a -> b) -> WindowSet sid i l a -> b
withCurrentStack dflt f = maybe dflt f . workspaceStack . screenWorkspace . current
modifyZ :: Maybe (Stack a) -> (Stack a -> Maybe (Stack a)) -> WindowSet sid i l a -> WindowSet sid i l a
modifyZ dflt f ws =
ws
{ current =
(current ws)
{ screenWorkspace =
(screenWorkspace (current ws))
{ workspaceStack = withCurrentStack dflt f ws
}
}
}
modifyZ' :: (Stack a -> Stack a) -> WindowSet sid i l a -> WindowSet sid i l a
modifyZ' f = modifyZ Nothing (Just . f)
peek :: WindowSet sid i l a -> Maybe a
peek = withCurrentStack Nothing (Just . stackFocus)
integrate :: Stack a -> [a]
integrate st = reverse (stackUp st) ++ stackFocus st : stackDown st
allWindows :: WindowSet sid i l a -> [a]
allWindows ws = concatMap workspaceWindows (workspaces ws)
where
workspaceWindows wk = maybe [] integrate (workspaceStack wk)
workspaces :: WindowSet sid i l a -> [Workspace i l a]
workspaces ws = screenWorkspace (current ws) : map screenWorkspace (visible ws) ++ hidden ws
member :: (Eq a) => a -> WindowSet sid i l a -> Bool
member win ws = win `elem` allWindows ws
workspaceMember :: (Eq i) => i -> WindowSet sid i l a -> Bool
workspaceMember wid ws = wid `elem` map workspaceId (workspaces ws)
findWorkspaceIdOfWindow :: (Eq a) => a -> WindowSet sid i l a -> Maybe i
findWorkspaceIdOfWindow win ws = workspaceId <$> find containsWindow (workspaces ws)
where
containsWindow wk = maybe False (elem win . integrate) (workspaceStack wk)
view :: (Eq i) => i -> WindowSet sid i l a -> WindowSet sid i l a
view wid ws
| workspaceId (screenWorkspace (current ws)) == wid = ws
| otherwise =
case break ((== wid) . workspaceId . screenWorkspace) (visible ws) of
(before, target : after) ->
ws
{ current = target
, visible = current ws : before ++ after
}
(_, []) ->
case break ((== wid) . workspaceId) (hidden ws) of
(before, target : after) ->
let oldCurrent = screenWorkspace (current ws)
in ws
{ current = (current ws) {screenWorkspace = target}
, hidden = before ++ [oldCurrent] ++ after
}
(_, []) -> ws
insertUp :: (Eq a) => a -> WindowSet sid i l a -> WindowSet sid i l a
insertUp win ws
| member win ws = ws
| otherwise = modifyZ (Just (Stack win [] [])) (\(Stack focus up down) -> Just (Stack win up (focus : down))) ws
focusWindow :: (Eq a, Eq i) => a -> WindowSet sid i l a -> WindowSet sid i l a
focusWindow win ws
| Just win == peek ws = ws
| otherwise =
fromMaybe ws $ do
wid <- findWorkspaceIdOfWindow win ws
pure $ until ((Just win ==) . peek) focusUp (view wid ws)
focusUp :: WindowSet sid i l a -> WindowSet sid i l a
focusUp = modifyZ' focusUpStack
focusDown :: WindowSet sid i l a -> WindowSet sid i l a
focusDown = modifyZ' focusDownStack
swapUp :: WindowSet sid i l a -> WindowSet sid i l a
swapUp = modifyZ' swapUpStack
swapDown :: WindowSet sid i l a -> WindowSet sid i l a
swapDown = modifyZ' swapDownStack
delete :: (Ord a) => a -> WindowSet sid i l a -> WindowSet sid i l a
delete win = sink win . delete' win
delete' :: (Eq a) => a -> WindowSet sid i l a -> WindowSet sid i l a
delete' win ws =
ws
{ current = removeFromScreen (current ws)
, visible = map removeFromScreen (visible ws)
, hidden = map removeFromWorkspace (hidden ws)
}
where
removeFromWorkspace wk = wk {workspaceStack = workspaceStack wk >>= filterStack (/= win)}
removeFromScreen scr = scr {screenWorkspace = removeFromWorkspace (screenWorkspace scr)}
sink :: (Ord a) => a -> WindowSet sid i l a -> WindowSet sid i l a
sink win ws = ws {floating = M.delete win (floating ws)}
filterStack :: (a -> Bool) -> Stack a -> Maybe (Stack a)
filterStack p (Stack focus up down) =
case filter p (focus : down) of
focus' : down' -> Just (Stack focus' (filter p up) down')
[] ->
case filter p up of
focus' : up' -> Just (Stack focus' up' [])
[] -> Nothing
focusUpStack :: Stack a -> Stack a
focusUpStack st = case stackUp st of
up : ups -> Stack up ups (stackFocus st : stackDown st)
[] ->
case reverse (stackFocus st : stackDown st) of
[] -> st
focus : rest -> Stack focus rest []
focusDownStack :: Stack a -> Stack a
focusDownStack = reverseStack . focusUpStack . reverseStack
swapUpStack :: Stack a -> Stack a
swapUpStack st = case stackUp st of
up : ups -> Stack (stackFocus st) ups (up : stackDown st)
[] -> Stack (stackFocus st) (reverse (stackDown st)) []
swapDownStack :: Stack a -> Stack a
swapDownStack = reverseStack . swapUpStack . reverseStack
reverseStack :: Stack a -> Stack a
reverseStack st = Stack (stackFocus st) (stackDown st) (stackUp st)
shift :: (Eq a, Eq i) => i -> WindowSet sid i l a -> WindowSet sid i l a
shift wid ws = case peek ws of
Nothing -> ws
Just win -> shiftWin wid win ws
shiftWin :: (Eq a, Eq i) => i -> a -> WindowSet sid i l a -> WindowSet sid i l a
shiftWin wid win ws = case findWorkspaceIdOfWindow win ws of
Just from
| workspaceMember wid ws && wid /= from -> move from ws
_ -> ws
where
move from = onWorkspace wid (insertUp win) . onWorkspace from (delete' win)
onWorkspace :: (Eq i) => i -> (WindowSet sid i l a -> WindowSet sid i l a) -> WindowSet sid i l a -> WindowSet sid i l a
onWorkspace wid f ws = view currentWid (f (view wid ws))
where
currentWid = workspaceId (screenWorkspace (current ws))