aboutsummaryrefslogtreecommitdiff
path: root/src/Rahm/Desktop/Layout/PinWindow.hs
blob: 959cc33ba22d4605cca9ac76f8b5af3c6dd44c5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{-# LANGUAGE TypeOperators #-}

module Rahm.Desktop.Layout.PinWindow where

import Control.Arrow (Arrow (second))
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 (Set Window))
  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
    (PinWindowState pinned) <- XS.get
    let allPinned = mconcat $ Map.elems pinned

    (W.StackSet cur vis _ floating) <- gets windowset

    (stack', winAndRect) <- case find ((== t) . (W.tag . W.workspace)) (cur : vis) of
      Just (W.Screen ws sid _) -> do
        let winsToMove = fromMaybe mempty (Map.lookup sid pinned)

        modifyWindowSet $
          appEndo $
            mconcat $ map (Endo . W.shiftWinNoFocus (W.tag ws)) (Set.toList winsToMove)

        updatedWorkspace <- withWindowSet $ return . W.findWorkspace t

        return
          ( maybe stack W.stack updatedWorkspace,
            map (second (scaleRationalRect rect)) $
              mapMaybe (\w -> (w,) <$> Map.lookup w floating) (Set.toList winsToMove)
          )
      Nothing -> return (stack, [])

    (rects, maybeNewLayout) <-
      runLayout (W.Workspace t l (W.filter (not . (`Map.member` floating)) =<< stack')) rect

    return
      ( winAndRect ++ rects,
        PinWindowLayout <$> maybeNewLayout
      )

pinWindow :: Window -> X ()
pinWindow win = runMaybeT_ $ do
  lift $ logs Debug "Pinning window %d" win

  ws@(W.StackSet cur vis _ _) <- gets windowset
  t <- hoist (W.findTag win ws)
  scr <- hoist $ find ((== t) . (W.tag . W.workspace)) (cur : vis)

  lift $
    XS.modify $ \(PinWindowState mp) ->
      PinWindowState $
        Map.alter
          (Just . maybe (Set.singleton win) (Set.insert win))
          (W.screen scr)
          mp
  where
    hoist = MaybeT . return

unpinWindow :: Window -> X ()
unpinWindow win = do
  XS.modify $ \(PinWindowState mp) ->
    PinWindowState $
      Map.map (Set.delete win) mp

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 (Set.member win) (Map.elems mp)