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 3426c240cb
Signed by: jasmine
GPG key ID: 8563E358D4E8040E
2 changed files with 53 additions and 28 deletions

View file

@ -90,12 +90,18 @@ 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 (inspired by i3wm)
- **Custom keybindings**: Leader-key approach (`M-a` prefix) for spawning applications and layout switching - Layout mode (`M-a l`): Unified mode for both layout operations
- `d/m/f` - Jump to layout (exits immediately)
- Arrow keys - Adjust master/slave split (stays in mode, `<Escape>` to exit)
- Spawn mode (`M-a s`): Launch infrequent applications (exits immediately)
- `t` - Thunar file manager
- `s` - Screenshot tool
- **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, spawnMode]
$ myConfig $ myConfig
myConfig = def myConfig = def
@ -65,23 +68,18 @@ myScrot = "scrot -s '%Y%m%d_%H%M%S.png' -e 'mv $f ~/Pictures/scrots
myKeymap = myKeymap =
-- --
-- LeaderKey -- Core operations
-- --
-- spawning programs -- spawn/kill programs
[ ("M-a n", spawn myTerminal) [ ("M-a n", spawn myTerminal)
, ("M-a e", spawn myLauncher) , ("M-a e", spawn myLauncher)
, ("M-a t", spawn myFileManager)
, ("M-a s", spawn myScrot )
-- toggling layouts
, ("M-a d", sendMessage $ JumpToLayout "dynamic tiling")
, ("M-a m", sendMessage $ JumpToLayout "maximised" )
, ("M-a f", sendMessage $ JumpToLayout "fullscreen" )
-- quit window
, ("M-a q", kill) , ("M-a q", kill)
-- modal modes
, ("M-a l", setMode "layout")
, ("M-a s", setMode "spawn" )
-- --
-- Navigation -- Navigation
@ -96,12 +94,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 +115,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 +133,30 @@ myKeymap =
] ]
--
-- Modal Modes
--
layoutMode :: Mode
layoutMode = mode "layout" $ mkKeysEz
-- jump to layout (exits immediately)
[ ("t", sendMessage (JumpToLayout "dynamic tiling") >> exitMode)
, ("m", sendMessage (JumpToLayout "maximised" ) >> exitMode)
, ("f", sendMessage (JumpToLayout "fullscreen" ) >> exitMode)
-- adjust master/slave split (stays in mode)
, ("<Left>", sendMessage Shrink)
, ("<Right>", sendMessage Expand)
, ("<Up>", sendMessage (IncMasterN 1))
, ("<Down>", sendMessage (IncMasterN (-1)))
]
spawnMode :: Mode
spawnMode = mode "spawn" $ mkKeysEz
[ ("t", spawn myFileManager >> exitMode)
, ("s", spawn myScrot >> exitMode)
]
-- --
-- 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 "-" 6 $ logMode
-- --