aboutsummaryrefslogtreecommitdiff
path: root/src/Rahm/Desktop/Layout
diff options
context:
space:
mode:
authorJosh Rahm <rahm@google.com>2023-12-08 16:14:05 -0700
committerJosh Rahm <rahm@google.com>2023-12-08 16:14:05 -0700
commit8bbc3ce0d0ae10b5e7630779c970f38f0a767789 (patch)
tree8d31ec2a97dd0ae880e2a5e3a2c29b8331d22976 /src/Rahm/Desktop/Layout
parent718d69736e5dfd946648e7a305c15281d9656466 (diff)
parent9f176adbff807dafec2caee5e3b104e65caf9029 (diff)
downloadrde-8bbc3ce0d0ae10b5e7630779c970f38f0a767789.tar.gz
rde-8bbc3ce0d0ae10b5e7630779c970f38f0a767789.tar.bz2
rde-8bbc3ce0d0ae10b5e7630779c970f38f0a767789.zip
Merge branch 'pinwindow'
Diffstat (limited to 'src/Rahm/Desktop/Layout')
-rw-r--r--src/Rahm/Desktop/Layout/PinWindow.hs149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/Rahm/Desktop/Layout/PinWindow.hs b/src/Rahm/Desktop/Layout/PinWindow.hs
new file mode 100644
index 0000000..fe98afd
--- /dev/null
+++ b/src/Rahm/Desktop/Layout/PinWindow.hs
@@ -0,0 +1,149 @@
+{-# LANGUAGE TypeOperators #-}
+
+module Rahm.Desktop.Layout.PinWindow where
+
+import Control.Arrow (Arrow (second))
+import Control.Exception (throw)
+import Control.Monad (unless, when)
+import Control.Monad.Trans (lift)
+import Control.Monad.Trans.Maybe (MaybeT (..))
+import Data.Foldable (find)
+import Data.List (nubBy)
+import Data.Map (Map)
+import qualified Data.Map as Map
+import Data.Maybe (fromMaybe, mapMaybe, maybe)
+import Data.Semigroup (Endo (..))
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Rahm.Desktop.Common (runMaybeT_)
+import Rahm.Desktop.Logger
+import qualified Rahm.Desktop.StackSet as W
+import XMonad
+import qualified XMonad.StackSet as W (filter)
+import qualified XMonad.Util.ExtensibleState as XS
+
+newtype PinWindowState = PinWindowState (Map ScreenId [(Window, W.RationalRect)])
+ deriving (Show, Read)
+
+instance Default PinWindowState where
+ def = PinWindowState mempty
+
+instance ExtensionClass PinWindowState where
+ initialValue = def
+ extensionType = PersistentExtension
+
+newtype PinWindowLayout (l :: * -> *) (a :: *) = PinWindowLayout (l a)
+ deriving (Show, Read)
+
+instance (LayoutClass l a, sid ~ ScreenId, a ~ Window) => LayoutClass (PinWindowLayout l) a where
+ runLayout (W.Workspace t (PinWindowLayout l) stack) rect = do
+ -- Clean up window id's thare should not be pinned anymore because the
+ -- windows were unmanaged.
+ (PinWindowState pinned') <- XS.get
+ ws <- gets windowset
+ XS.put $ PinWindowState (cleanupPinned pinned' ws)
+ (PinWindowState pinned) <- XS.get
+
+ let myScreen = find ((== t) . W.tag . W.workspace) (W.screens ws)
+ pinnedRects =
+ maybe
+ []
+ (map $ second (scaleRationalRect rect))
+ (((`Map.lookup` pinned) . W.screen) =<< myScreen)
+ allPinned = Set.fromList $ map fst $ concat $ Map.elems pinned
+ pinnedOnMyScreen = map fst pinnedRects
+
+ windowsToMove =
+ filter (\win -> W.findTag win ws /= Just t) pinnedOnMyScreen
+
+ modifyWindowSet $
+ appEndo . mconcat $
+ -- Move the windows that are supposed to now be on this workspace here
+ map (Endo . W.shiftWinNoFocus t) windowsToMove
+ ++
+ -- once again, sink the windows that are supposed to be here. Make
+ -- sure they don't float.
+ map (Endo . W.sink) (Set.toList allPinned)
+
+ (rects, maybeNewLayout) <-
+ runLayout
+ (W.Workspace t l (W.filter (not . (`Set.member` allPinned)) =<< stack))
+ rect
+
+ return (pinnedRects ++ rects, PinWindowLayout <$> maybeNewLayout)
+ where
+ cleanupPinned mp ss =
+ let aw = Set.fromList (W.allWindows ss)
+ in Map.map (filter ((`Set.member` aw) . fst)) mp
+
+ handleMessage (PinWindowLayout l) a = do
+ maybeNewLayout <- handleMessage l a
+ return (PinWindowLayout <$> maybeNewLayout)
+
+pinWindow :: Window -> X ()
+pinWindow win = runMaybeT_ $ do
+ lift $ logs Debug "Pinning window %d" win
+
+ ws@(W.StackSet cur vis _ flt) <- gets windowset
+ t <- hoist (W.findTag win ws)
+ scr <- hoist $ find ((== t) . (W.tag . W.workspace)) (cur : vis)
+ rect <- hoist $ Map.lookup win flt
+
+ lift $ do
+ XS.modify $ \(PinWindowState mp) ->
+ PinWindowState $
+ Map.alter
+ (Just . maybe [(win, rect)] ((win, rect) :))
+ (W.screen scr)
+ mp
+
+ -- Don't float the window anymore.
+ modifyWindowSet $ W.sink win
+ where
+ hoist = MaybeT . return
+
+unpinWindow :: Window -> X ()
+unpinWindow win = runMaybeT_ $ do
+ (PinWindowState mp) <- lift XS.get
+ (win, rect) <- hoist $ find ((== win) . fst) (concat $ Map.elems mp)
+
+ lift $ do
+ XS.put $
+ PinWindowState $
+ Map.map (filter ((/= win) . fst)) mp
+
+ -- refloat the window.
+ modifyWindowSet $ W.float win rect
+ where
+ hoist = MaybeT . return
+
+toggleWindowPin :: Window -> X ()
+toggleWindowPin win = do
+ isPinned <- isWindowPinned win
+ if isPinned
+ then unpinWindow win
+ else pinWindow win
+
+isWindowPinned :: Window -> X Bool
+isWindowPinned win = do
+ (PinWindowState mp) <- XS.get
+ return $ any (any $ (== win) . fst) (Map.elems mp)
+
+pinnedWindows :: X [Window]
+pinnedWindows = do
+ (PinWindowState s) <- XS.get
+ return $ map fst $ concat $ Map.elems s
+
+pinnable :: l a -> PinWindowLayout l a
+pinnable = PinWindowLayout
+
+-- Unpins the window, executes the action, then repins the window. Useful for
+-- window shifts and whatnot.
+withWindowsUnpinned :: [Window] -> X () -> X ()
+withWindowsUnpinned wins fn =
+ ( do
+ mapM_ unpinWindow wins
+ fn
+ mapM_ pinWindow wins
+ )
+ `catchX` mapM_ pinWindow wins