aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/ui-navigation.el38
-rw-r--r--tests/test-ui-navigation--window-resize.el39
2 files changed, 69 insertions, 8 deletions
diff --git a/modules/ui-navigation.el b/modules/ui-navigation.el
index d8d7162e2..00fa841e1 100644
--- a/modules/ui-navigation.el
+++ b/modules/ui-navigation.el
@@ -75,14 +75,42 @@ resize -- each moves the active window's divider in the arrow's direction
"<up>" #'windsize-up
"<down>" #'windsize-down)
+(defun cj/window-arrow-direction (key)
+ "Map a windsize arrow KEY description to a split direction.
+KEY is one of \"<left>\" \"<right>\" \"<up>\" \"<down>\"; returns
+left/right/above/below respectively, or nil for anything else."
+ (pcase key
+ ("<left>" 'left)
+ ("<right>" 'right)
+ ("<up>" 'above)
+ ("<down>" 'below)
+ (_ 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)))
+ (set-window-buffer new (other-buffer (current-buffer) t))
+ new)))
+
(defun cj/window-resize-sticky ()
"Resize the active window's divider in the just-pressed arrow's direction
-(via `windsize'), then keep `cj/window-resize-map' active so bare arrows keep
-nudging until any other key. Bound to `C-; b <left>/<right>/<up>/<down>'."
+\(via `windsize'), then keep `cj/window-resize-map' active so bare arrows keep
+nudging until any other key. Bound to `C-; b <left>/<right>/<up>/<down>'.
+
+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."
(interactive)
- (let ((cmd (keymap-lookup cj/window-resize-map
- (key-description (vector last-command-event)))))
- (when cmd (call-interactively cmd)))
+ (let ((key (key-description (vector last-command-event))))
+ (if (one-window-p)
+ (cj/window--pull-away (cj/window-arrow-direction key))
+ (let ((cmd (keymap-lookup cj/window-resize-map key)))
+ (when cmd (call-interactively cmd)))))
(set-transient-map cj/window-resize-map t))
;; ------------------------------ Window Splitting -----------------------------
diff --git a/tests/test-ui-navigation--window-resize.el b/tests/test-ui-navigation--window-resize.el
index 3be0313b8..986ce1580 100644
--- a/tests/test-ui-navigation--window-resize.el
+++ b/tests/test-ui-navigation--window-resize.el
@@ -24,8 +24,11 @@
(should (eq (keymap-lookup cj/window-resize-map "<down>") #'windsize-down)))
(ert-deftest test-ui-navigation-window-resize-sticky-dispatches-and-arms ()
- "Normal: `cj/window-resize-sticky' runs the `windsize' command matching the
-arrow key that triggered it, then arms the sticky-repeat map."
+ "Normal: with more than one window, `cj/window-resize-sticky' runs the
+`windsize' command matching the arrow key that triggered it, then arms the
+sticky-repeat map. `one-window-p' is forced nil so the resize path is taken
+deterministically -- in `--batch' the sole frame is one-window-p, which would
+otherwise route to the pull-away path."
(dolist (case '((left . windsize-left)
(right . windsize-right)
(up . windsize-up)
@@ -33,13 +36,43 @@ arrow key that triggered it, then arms the sticky-repeat map."
(let ((ran nil)
(overriding-terminal-local-map nil)
(pre-command-hook nil))
- (cl-letf (((symbol-function (cdr case))
+ (cl-letf (((symbol-function 'one-window-p) (lambda (&rest _) nil))
+ ((symbol-function (cdr case))
(lambda (&rest _) (interactive) (setq ran t))))
(let ((last-command-event (car case)))
(cj/window-resize-sticky)))
(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>") 'left))
+ (should (eq (cj/window-arrow-direction "<right>") 'right))
+ (should (eq (cj/window-arrow-direction "<up>") 'above))
+ (should (eq (cj/window-arrow-direction "<down>") 'below))
+ (should (null (cj/window-arrow-direction "<prior>")))
+ (should (null (cj/window-arrow-direction "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)))
+ (let ((pulled nil)
+ (overriding-terminal-local-map nil)
+ (pre-command-hook nil))
+ (cl-letf (((symbol-function 'one-window-p) (lambda (&rest _) t))
+ ((symbol-function 'cj/window--pull-away)
+ (lambda (dir) (setq pulled dir))))
+ (let ((last-command-event (car case)))
+ (cj/window-resize-sticky)))
+ (should (eq pulled (cdr case))) ; pulled toward the arrow
+ (should overriding-terminal-local-map)))) ; loop armed
+
(ert-deftest test-ui-navigation-window-resize-bound-under-c-semicolon-b ()
"Normal: `C-; b <arrow>' (each direction) reaches the sticky-resize command."
(require 'custom-buffer-file)