From 9e069a310d14f9b6ebbe6e3747240e7844d9ffcb Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 20 Jun 2026 10:51:51 -0400 Subject: fix(windows): keep the pulled-away window on the arrow's edge The sole-window pull split toward the arrow at 50/50, so a fullscreen terminal jumped above the revealed buffer at half height. Now the reveal opens on the opposite side and is minimized to a sliver, so the current window keeps the arrow's edge near-full and the sticky windsize arrows shrink it step by step, matching the feel of resizing an existing split. --- modules/ui-navigation.el | 46 ++++++++++++++++++------------ tests/test-ui-navigation--window-resize.el | 34 +++++++++++----------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/modules/ui-navigation.el b/modules/ui-navigation.el index 00fa841e1..f53924ebb 100644 --- a/modules/ui-navigation.el +++ b/modules/ui-navigation.el @@ -75,26 +75,31 @@ resize -- each moves the active window's divider in the arrow's direction "" #'windsize-up "" #'windsize-down) -(defun cj/window-arrow-direction (key) - "Map a windsize arrow KEY description to a split direction. -KEY is one of \"\" \"\" \"\" \"\"; returns -left/right/above/below respectively, or nil for anything else." +(defun cj/window-pull-side (key) + "Map a `C-; b' arrow KEY to the side the revealed window opens on. +The arrow names the edge the current window shrinks toward, so the new +window opens on the *opposite* side and the current window keeps the +arrow's edge: -> above, -> below, -> right, + -> left. Returns nil for anything else." (pcase key - ("" 'left) - ("" 'right) - ("" 'above) - ("" 'below) + ("" 'above) + ("" 'below) + ("" 'right) + ("" 'left) (_ nil))) -(defun cj/window--pull-away (direction) - "Split the sole window toward DIRECTION and reveal the previous buffer. -DIRECTION is one of left/right/above/below. A new window opens on that -side showing `other-buffer'; focus stays on the original window so it -shrinks from that edge, letting a fullscreen window (e.g. a terminal) -share the frame. No-op when DIRECTION is nil." - (when direction - (let ((new (split-window (selected-window) nil direction))) +(defun cj/window--pull-away (side) + "Split the sole window so the previous buffer opens on SIDE. +SIDE is one of above/below/left/right -- opposite the pressed arrow, so +the current window keeps the arrow's edge. The new window is minimized +to a sliver (the current window keeps almost the whole frame) and shows +`other-buffer'; focus stays on the current window so the sticky arrows +then shrink it step by step via `windsize', exactly as resizing an +existing split does. No-op when SIDE is nil." + (when side + (let ((new (split-window (selected-window) nil side))) (set-window-buffer new (other-buffer (current-buffer) t)) + (minimize-window new) new))) (defun cj/window-resize-sticky () @@ -103,12 +108,15 @@ share the frame. No-op when DIRECTION is nil." nudging until any other key. Bound to `C-; b ///'. When the selected window is the sole window in the frame there is no -divider to move, so the arrow instead pulls a new window away toward that -edge (`cj/window--pull-away'), revealing the previous buffer." +divider to move, so the first arrow instead splits a sliver away on the +side opposite the arrow (`cj/window--pull-away'), revealing the previous +buffer; the current window keeps almost the whole frame and the following +arrows shrink it via `windsize', so it reads the same as resizing an +existing split." (interactive) (let ((key (key-description (vector last-command-event)))) (if (one-window-p) - (cj/window--pull-away (cj/window-arrow-direction key)) + (cj/window--pull-away (cj/window-pull-side key)) (let ((cmd (keymap-lookup cj/window-resize-map key))) (when cmd (call-interactively cmd))))) (set-transient-map cj/window-resize-map t)) diff --git a/tests/test-ui-navigation--window-resize.el b/tests/test-ui-navigation--window-resize.el index 986ce1580..553219755 100644 --- a/tests/test-ui-navigation--window-resize.el +++ b/tests/test-ui-navigation--window-resize.el @@ -44,24 +44,26 @@ otherwise route to the pull-away path." (should ran) ; dispatched to the right command (should overriding-terminal-local-map)))) ; loop armed -(ert-deftest test-ui-navigation-window-arrow-direction () - "Normal/Error: each arrow maps to its split direction; anything else is nil." - (should (eq (cj/window-arrow-direction "") 'left)) - (should (eq (cj/window-arrow-direction "") 'right)) - (should (eq (cj/window-arrow-direction "") 'above)) - (should (eq (cj/window-arrow-direction "") 'below)) - (should (null (cj/window-arrow-direction ""))) - (should (null (cj/window-arrow-direction "x")))) +(ert-deftest test-ui-navigation-window-pull-side () + "Normal/Error: each arrow maps to the *opposite* side (where the revealed +window opens, so the current window keeps the arrow's edge); anything else +is nil." + (should (eq (cj/window-pull-side "") 'above)) + (should (eq (cj/window-pull-side "") 'below)) + (should (eq (cj/window-pull-side "") 'right)) + (should (eq (cj/window-pull-side "") 'left)) + (should (null (cj/window-pull-side ""))) + (should (null (cj/window-pull-side "x")))) (ert-deftest test-ui-navigation-window-resize-sticky-sole-window-pulls-away () - "Normal: with a single window, the arrow pulls a window away toward its -direction (via `cj/window--pull-away') rather than resizing, then arms the -loop. `cj/window--pull-away' is stubbed to capture the direction so no real -window split happens under `--batch'." - (dolist (case '((left . left) - (right . right) - (up . above) - (down . below))) + "Normal: with a single window, the arrow pulls a sliver away on the side +opposite the arrow (via `cj/window--pull-away') rather than resizing, then +arms the loop. `cj/window--pull-away' is stubbed to capture the side so no +real window split happens under `--batch'." + (dolist (case '((down . above) + (up . below) + (left . right) + (right . left))) (let ((pulled nil) (overriding-terminal-local-map nil) (pre-command-hook nil)) -- cgit v1.2.3