aboutsummaryrefslogtreecommitdiff
path: root/src/Rahm/Desktop/Layout/Hole.hs
blob: 8bebb36e9452d74e65b9a045691b974424b4c820 (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- Delegates to a lower layout, but leaves a hole where the next window will go.
module Rahm.Desktop.Layout.Hole
  ( hole,
    addHoleForWindow,
    removeHoleForWindow,
    resetHole,
  )
where

import Control.Monad (forM_)
import Data.List (intercalate, sortOn)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Rahm.Desktop.Logger (LogLevel (Debug, Trace), logs)
import Rahm.Desktop.StackSet (TilePosition (TilePosition))
import qualified Rahm.Desktop.StackSet as W
import XMonad
  ( LayoutClass (handleMessage, runLayout),
    Message,
    Window,
    WorkspaceId,
    X,
    fromMessage,
  )

data Hole (l :: * -> *) (a :: *)
  = Hole (Map WorkspaceId [(W.TilePosition WorkspaceId, Window)]) (l a)

deriving instance Show (l a) => Show (Hole l a)

deriving instance Read (l a) => Read (Hole l a)

hole :: l a -> Hole l a
hole = Hole mempty

resetHole :: ManageHole
resetHole = ManageHole $ \(Hole _ l) -> Hole mempty l

addHoleForWindow :: W.TilePosition WorkspaceId -> Window -> ManageHole
addHoleForWindow p@(W.TilePosition wid _) win = ManageHole $ \(Hole m l) ->
  Hole
    ( Map.alter
        ( \(fromMaybe [] -> existing) ->
            Just $ (p, win) : existing
        )
        wid
        m
    )
    l

removeHoleForWindow :: Window -> ManageHole
removeHoleForWindow win = ManageHole $ \(Hole m l) ->
  Hole
    ( Map.mapMaybe
        (Just . filter ((/= win) . snd))
        m
    )
    l

dbgHole :: Hole l a -> X ()
dbgHole (Hole mp _) = do
  logs Trace "Hole:"
  forM_ (Map.toList mp) $ \(wid, poses) ->
    logs Trace "  wid[%s] - [%s]" wid $
      intercalate
        ","
        ( map (\(TilePosition _ n, w) -> show w ++ "@" ++ show n) poses
        )

-- toggleHole :: ManageHole
-- toggleHole = ManageHole $ \(Hole e l) -> Hole (not e) l

data ManageHole where
  ManageHole :: (forall l a. Hole l a -> Hole l a) -> ManageHole
  deriving (Message)

instance (LayoutClass l a, a ~ Window) => LayoutClass (Hole l) a where
  runLayout (W.Workspace t h@(Hole holes l) a) rect = do
    dbgHole h
    (rects, maybeNewLayout) <- runLayout (app holes $ W.Workspace t l a) rect
    return (filter ((> 0) . fst) rects, fmap (Hole holes) maybeNewLayout)
    where
      app mp (W.Workspace t l (Just s))
        | Just positions <- sortIt <$> Map.lookup t mp =
          let integrated = W.integrate s
           in W.Workspace t l $
                W.differentiateWithFocus (W.focus s) $
                  reverse $
                    addr integrated $
                      foldl
                        ( \((idx, pos, fakeid), ret) w ->
                            case pos of
                              ((TilePosition _ n, win) : tpos)
                                | n == idx && win `notElem` integrated ->
                                  ((idx + 1, tpos, fakeid - 1), w : fakeid : ret)
                              _ -> ((idx + 1, pos, fakeid), w : ret)
                        )
                        ((0, positions, -1), [])
                        integrated
      app _ w = w

      sortIt = sortOn (\(TilePosition _ p, _) -> p)

      addr integrated ((idx, pos, fakeid), ret) =
        case pos of
          ((TilePosition _ n, win) : _) | n == idx && win `notElem` integrated -> fakeid : ret
          _ -> ret

  handleMessage h (fromMessage -> Just (ManageHole f)) =
    return $ Just $ f h
  handleMessage (Hole e l) a = do
    maybeNewLayout <- handleMessage l a
    return (Hole e <$> maybeNewLayout)