From e057f8b90852bfbdef9e5520e5648c0490fbeb21 Mon Sep 17 00:00:00 2001 From: jasmine Date: Fri, 3 Oct 2025 17:30:29 +0800 Subject: [PATCH] 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 --- CLAUDE.md | 10 ++++++---- src/xmonad.hs | 49 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3c0876a..4b36be2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,12 +90,14 @@ main = xmonad **Key architectural components:** -- **Navigation2D**: Directional window and screen navigation using arrow keys and Navigation2D module -- **Custom keybindings**: Leader-key approach (`M-a` prefix) for spawning applications and layout switching +- **Modal keybindings**: Uses `XMonad.Hooks.Modal` for organizing keybindings into modes + - 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) - **Layout system**: Three layouts - dynamic tiling, maximised, and fullscreen -- **StatusBar integration**: Communicates with xmobar via `_XMONAD_LOG_1` X property -- **Mnemonic bindings**: Control (M-C) for layout manipulation, Shift (M-S) for window operations +- **StatusBar integration**: Communicates with xmobar via `_XMONAD_LOG_1` X property, displays active mode via `logMode` +- **Multimedia keys**: Uses `Graphics.X11.ExtraTypes.XF86` for XF86 multimedia key support (audio controls) ### XMobar Configuration (`src/xmobar.hs`) diff --git a/src/xmonad.hs b/src/xmonad.hs index 03864d7..5b6e9e7 100644 --- a/src/xmonad.hs +++ b/src/xmonad.hs @@ -6,12 +6,14 @@ import XMonad.Actions.RotSlaves import XMonad.Hooks.DynamicLog import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.ManageDocks +import XMonad.Hooks.Modal import XMonad.Hooks.StatusBar import XMonad.Layout.NoBorders import XMonad.Layout.Renamed import XMonad.Layout.Spacing import XMonad.StackSet qualified as W import XMonad.Util.EZConfig +import XMonad.Util.Font import XMonad.Util.Loggers import Graphics.X11.ExtraTypes.XF86 @@ -30,6 +32,7 @@ main = xmonad . ewmhFullscreen . ewmh . withSB myXmobar + . modal [layoutMode, masterMode] $ myConfig myConfig = def @@ -74,10 +77,9 @@ myKeymap = , ("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" ) + -- modal modes + , ("M-a l", setMode "layout") + , ("M-a m", setMode "master") -- quit window , ("M-a q", kill) @@ -96,12 +98,12 @@ myKeymap = , ("M-", moveTo Next hiddenWS) -- window rotation - , ("M-S-", rotAllUp) - , ("M-S-", rotAllDown) + , ("M-" , rotAllUp) + , ("M-", rotAllDown) -- focus screens - , ("M-S-", prevScreen) - , ("M-S-", nextScreen) + , ("M-", prevScreen) + , ("M-" , nextScreen) -- switch workspaces , ("M-1", windows $ W.greedyView "code" ) @@ -117,12 +119,6 @@ myKeymap = , ("M-S-4", windows $ W.shift "games") , ("M-S-5", windows $ W.shift "misc" ) - -- master pane manipulation - , ("M-", sendMessage Shrink) -- shrink master pane width - , ("M-", sendMessage Expand) -- expand master pane width - , ("M-", sendMessage (IncMasterN 1)) -- more windows in master - , ("M-", sendMessage (IncMasterN (-1))) -- fewer windows in master - -- master window operations , ("M-", windows W.focusMaster) , ("M-S-", 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 + [ ("", sendMessage Shrink) + , ("", sendMessage Expand) + , ("", sendMessage (IncMasterN 1)) + , ("", sendMessage (IncMasterN (-1))) + ] + + -- -- Layouts -- @@ -175,8 +191,11 @@ myXmobarPP = def , ppHiddenNoWindows = grey0 . wrap " " "" , ppUrgent = red . wrap " " "" , ppLayout = aqua . wrap (grey0 " [ ") (grey0 " ] ") - , 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 --