feat: implement modal keybindings for layout and master pane control

Add XMonad.Hooks.Modal to separate layout switching and master pane
manipulation into distinct modal namespaces. This resolves keybinding
conflicts and creates a more organized interaction model.

Changes:
- Layout mode (M-a l): one-shot layout selection (d/m/f)
- Master mode (M-a m): persistent pane adjustments with arrow keys
- Restore ppExtras with logMode to display active mode in xmobar
- Remove conflicting Home/End/PageUp/PageDown bindings from master pane
- Simplify navigation by moving screen focus back to Home/End
This commit is contained in:
♥ Minnie ♥ 2025-10-03 17:30:29 +08:00
parent 9ce0ee6dcc
commit e057f8b908
Signed by: jasmine
GPG key ID: 8563E358D4E8040E
2 changed files with 40 additions and 19 deletions

View file

@ -90,12 +90,14 @@ main = xmonad
**Key architectural components:** **Key architectural components:**
- **Navigation2D**: Directional window and screen navigation using arrow keys and Navigation2D module - **Modal keybindings**: Uses `XMonad.Hooks.Modal` for organizing keybindings into modes
- **Custom keybindings**: Leader-key approach (`M-a` prefix) for spawning applications and layout switching - Layout mode (`M-a l`): One-shot layout selection (d/m/f)
- Master mode (`M-a m`): Persistent master pane adjustments with arrow keys
- **Leader-key approach**: `M-a` prefix for spawning applications and entering modes
- **Window manipulation**: Uses `XMonad.Actions.RotSlaves` for window rotation (mimics wezterm behavior) - **Window manipulation**: Uses `XMonad.Actions.RotSlaves` for window rotation (mimics wezterm behavior)
- **Layout system**: Three layouts - dynamic tiling, maximised, and fullscreen - **Layout system**: Three layouts - dynamic tiling, maximised, and fullscreen
- **StatusBar integration**: Communicates with xmobar via `_XMONAD_LOG_1` X property - **StatusBar integration**: Communicates with xmobar via `_XMONAD_LOG_1` X property, displays active mode via `logMode`
- **Mnemonic bindings**: Control (M-C) for layout manipulation, Shift (M-S) for window operations - **Multimedia keys**: Uses `Graphics.X11.ExtraTypes.XF86` for XF86 multimedia key support (audio controls)
### XMobar Configuration (`src/xmobar.hs`) ### XMobar Configuration (`src/xmobar.hs`)

View file

@ -6,12 +6,14 @@ import XMonad.Actions.RotSlaves
import XMonad.Hooks.DynamicLog import XMonad.Hooks.DynamicLog
import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageDocks import XMonad.Hooks.ManageDocks
import XMonad.Hooks.Modal
import XMonad.Hooks.StatusBar import XMonad.Hooks.StatusBar
import XMonad.Layout.NoBorders import XMonad.Layout.NoBorders
import XMonad.Layout.Renamed import XMonad.Layout.Renamed
import XMonad.Layout.Spacing import XMonad.Layout.Spacing
import XMonad.StackSet qualified as W import XMonad.StackSet qualified as W
import XMonad.Util.EZConfig import XMonad.Util.EZConfig
import XMonad.Util.Font
import XMonad.Util.Loggers import XMonad.Util.Loggers
import Graphics.X11.ExtraTypes.XF86 import Graphics.X11.ExtraTypes.XF86
@ -30,6 +32,7 @@ main = xmonad
. ewmhFullscreen . ewmhFullscreen
. ewmh . ewmh
. withSB myXmobar . withSB myXmobar
. modal [layoutMode, masterMode]
$ myConfig $ myConfig
myConfig = def myConfig = def
@ -74,10 +77,9 @@ myKeymap =
, ("M-a t", spawn myFileManager) , ("M-a t", spawn myFileManager)
, ("M-a s", spawn myScrot ) , ("M-a s", spawn myScrot )
-- toggling layouts -- modal modes
, ("M-a d", sendMessage $ JumpToLayout "dynamic tiling") , ("M-a l", setMode "layout")
, ("M-a m", sendMessage $ JumpToLayout "maximised" ) , ("M-a m", setMode "master")
, ("M-a f", sendMessage $ JumpToLayout "fullscreen" )
-- quit window -- quit window
, ("M-a q", kill) , ("M-a q", kill)
@ -96,12 +98,12 @@ myKeymap =
, ("M-<Right>", moveTo Next hiddenWS) , ("M-<Right>", moveTo Next hiddenWS)
-- window rotation -- window rotation
, ("M-S-<Up>", rotAllUp) , ("M-<Page_Up>" , rotAllUp)
, ("M-S-<Down>", rotAllDown) , ("M-<Page_Down>", rotAllDown)
-- focus screens -- focus screens
, ("M-S-<Left>", prevScreen) , ("M-<Home>", prevScreen)
, ("M-S-<Right>", nextScreen) , ("M-<End>" , nextScreen)
-- switch workspaces -- switch workspaces
, ("M-1", windows $ W.greedyView "code" ) , ("M-1", windows $ W.greedyView "code" )
@ -117,12 +119,6 @@ myKeymap =
, ("M-S-4", windows $ W.shift "games") , ("M-S-4", windows $ W.shift "games")
, ("M-S-5", windows $ W.shift "misc" ) , ("M-S-5", windows $ W.shift "misc" )
-- master pane manipulation
, ("M-<Home>", sendMessage Shrink) -- shrink master pane width
, ("M-<End>", sendMessage Expand) -- expand master pane width
, ("M-<Page_Up>", sendMessage (IncMasterN 1)) -- more windows in master
, ("M-<Page_Down>", sendMessage (IncMasterN (-1))) -- fewer windows in master
-- master window operations -- master window operations
, ("M-<Delete>", windows W.focusMaster) , ("M-<Delete>", windows W.focusMaster)
, ("M-S-<Delete>", windows W.swapMaster ) , ("M-S-<Delete>", windows W.swapMaster )
@ -141,6 +137,26 @@ myKeymap =
] ]
--
-- Modal Modes
--
layoutMode :: Mode
layoutMode = mode "layout" $ mkKeysEz
[ ("d", sendMessage (JumpToLayout "dynamic tiling") >> exitMode)
, ("m", sendMessage (JumpToLayout "maximised" ) >> exitMode)
, ("f", sendMessage (JumpToLayout "fullscreen" ) >> exitMode)
]
masterMode :: Mode
masterMode = mode "master" $ mkKeysEz
[ ("<Left>", sendMessage Shrink)
, ("<Right>", sendMessage Expand)
, ("<Up>", sendMessage (IncMasterN 1))
, ("<Down>", sendMessage (IncMasterN (-1)))
]
-- --
-- Layouts -- Layouts
-- --
@ -175,8 +191,11 @@ myXmobarPP = def
, ppHiddenNoWindows = grey0 . wrap " " "" , ppHiddenNoWindows = grey0 . wrap " " ""
, ppUrgent = red . wrap " " "" , ppUrgent = red . wrap " " ""
, ppLayout = aqua . wrap (grey0 " <fn=1>[</fn> ") (grey0 " <fn=1>]</fn> ") , ppLayout = aqua . wrap (grey0 " <fn=1>[</fn> ") (grey0 " <fn=1>]</fn> ")
, ppOrder = \[ws, l, _] -> [ws, l] , ppOrder = \case { [ws, l, title, mode] -> [ws, l, mode, title]; xs -> xs }
, ppExtras = [lMode]
} }
where
lMode = xmobarColorL "#d8a657" "#282828" . fixedWidthL AlignCenter "-" 4 $ logMode
-- --