emacs-31 4be321d5115: Fix issues related to window layout changes (Bug#81406)
Martin Rudalics via Mailing list for Emacs changes <[email protected]> Tue, 14 Jul 2026 05:24:47 -0400 (EDT)
| Newsgroups | gmane.emacs.diffs |
|---|---|
| Message-ID | <[email protected]> |
branch: emacs-31 commit 4be321d5115d53c8df1a90602d1465854b68627b Author: Martin Rudalics <[email protected]> Commit: Martin Rudalics <[email protected]> Fix issues related to window layout changes (Bug#81406) * src/window.c (Vwindow_combination_resize): In doc-string say that binding this to nil may be necessary to produce specific, predefined frame layouts (Bug#81406). * lisp/window-x.el (window-layout-rotate-clockwise) (window-layout-rotate-anticlockwise) (window-layout-flip-leftright, window-layout-flip-topdown) (window-layout-transpose, rotate-windows-change-selected) (rotate-windows, rotate-windows-back): Rewrite doc-strings. Make sure that non-interactive calls with nil or absent WINDOW argument work on the frame's main window. Fix infinite loop when 'rotate-windows' is called for a non-selected frame. * doc/lispref/windows.texi (Recombining Windows): Say that binding 'window-combination-resize' to nil may be necessary to produce specific, predefined frame layouts (Bug#81406). (Changing Window Layouts): Minor rewrite. * etc/NEWS: Announce names of new commands to change window layout (Bug#81406). --- doc/lispref/windows.texi | 31 +++++--- etc/NEWS | 17 +++-- lisp/window-x.el | 195 ++++++++++++++++++++++++++--------------------- src/window.c | 5 +- 4 files changed, 144 insertions(+), 104 deletions(-) diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 9caaa5e6901..137a112acf6 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2025,6 +2025,13 @@ combination. The default is @code{nil}. Other values are reserved for future use. A specific split operation may ignore the value of this variable if it is affected by a non-@code{nil} value of @code{window-combination-limit}. + +If you plan to use a sequence of @code{split-window} calls to produce a +specific, predefined layout of windows on a frame, bind this variable +temporarily to nil. @code{window-state-put} (@pxref{Window +Configurations}) and the functions to change window layouts +(@pxref{Changing Window Layouts}) do that in order to restore a previous +or systematically changed layout of a frame. @end defopt To illustrate the effect of @code{window-combination-resize}, consider @@ -2302,8 +2309,8 @@ below, we shall always assume that the windows displayed constitute their frame's main window and the @var{window} argument is @code{nil} unless stated otherwise. - The first two commands rotate a window layout either clockwise or -counterclockwise. + The first two commands rotate an entire window layout either +clockwise or counterclockwise. @cindex rotate window layout @deffn Command window-layout-rotate-clockwise &optional window @@ -2368,7 +2375,7 @@ windows on the top and vice-versa as in the example below. @deffn Command window-layout-flip-leftright &optional window This command rearranges window in a way that the windows on the right -become the window on the left, and vice-versa. +become the windows on the left, and vice-versa. @smallexample @group @@ -2409,13 +2416,19 @@ becomes a vertical split and vice versa. The final two commands can be used to rotate windows within the existing layout. They are like the commands that rotate the layout but leave the underlying structure of the layout unchanged. What actually changes are -the positions of windows within the existing layout. +the positions of windows within the existing layout. This happens in a +way that the space formerly occupied by any window will be occupied by +the window preceding (following if @var{reverse} is non-nil) it in the +cyclic ordering of windows (@pxref{Cyclic Window Ordering}) on +@var{window}'s frame. These commands may signal an error if windows +chosen to rotate are dedicated to their buffers, atomic or of fixed +size. @cindex rotating windows @deffn Command rotate-windows &optional window reverse -This rotate windows under @var{window} in cyclic ordering. The optional -argument @var{reverse} means to rotate windows backward, in reverse -cyclic order. +This command rotates @var{window}'c child windows in cyclic ordering. +The optional argument @var{reverse} means to rotate windows backwards, +in reverse cyclic order. @smallexample @group @@ -2432,7 +2445,7 @@ cyclic order. @end deffn @deffn Command rotate-windows-back &optional window -This command rotates windows under @var{window} backward in cyclic +This command rotates @var{window}'s child windows backwards in cyclic ordering. @smallexample @@ -2455,7 +2468,7 @@ The last two commands are subject to the following option. If this is @code{nil}, the selected window will remain unaffected by @code{rotate-windows} and @code{rotate-windows-back}. Otherwise, the selected window will change to the window that appears at the location -of the selected window before any of these commands were invoked. +of the window selected before any of these commands were invoked. @end defopt diff --git a/etc/NEWS b/etc/NEWS index 4609e9b6910..9dcb38f05bb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -357,14 +357,15 @@ increments as with Emacs 24 through Emacs 30 you now have to set ** Windows +++ -*** New commands to modify window layouts. - -- 'C-x w t' and 'C-x w r <left>/<right>' rotate the window layout. -- 'C-x w o <left>/<right>' rotate the windows within the current layout. -- 'C-x w f <left>/<right>/<up>/<down>' flip window layouts. - -By default, these commands operate on the selected frame's root window. -With a prefix argument, they operate on the selected window's parent. +*** New commands to modify window layouts of frames. +'window-layout-rotate-clockwise' and its counterpart +'window-layout-rotate-anticlockwise' rotate an entire window layout. +'window-layout-flip-topdown' and 'window-layout-flip-leftright' flip the +window layout vertically and horizontally. +'window-layout-transpose' reorganizes windows such that every horizontal +split becomes a vertical split and vice versa. +'rotate-windows' and its counterpart 'rotate-windows-back' rotate +windows in cylic ordering. +++ *** Windmove commands now move to skipped windows if invoked twice in a row. diff --git a/lisp/window-x.el b/lisp/window-x.el index 21a1f422562..82caabc3631 100644 --- a/lisp/window-x.el +++ b/lisp/window-x.el @@ -29,17 +29,8 @@ ;;; Code: -(defcustom rotate-windows-change-selected t - "If nil the selected window will not change with `rotate-windows'. - -The selected window before and after the function call will stay -unchanged if nil. `rotate-windows-back' is also affected." - :type 'boolean - :group 'windows) - (defun window-tree-normal-sizes (window &optional next) "Return normal sizes of all windows rooted at WINDOW. - The return value is a list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT PARENT-WIN-WIDTH . WS), where SPLIT-TYPE is non-nil if PARENT-WIN is split horizontally; PARENT-WIN is the internal window; @@ -47,7 +38,7 @@ PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of PARENT-WIN; and WS is a list of lists the form (WINDOW HEIGHT WIDTH) where HEIGHT and WIDTH are the normal height and width of the window. -(fn WINDOW)" +\(fn WINDOW)" (let (list) (while window (setq list @@ -77,97 +68,112 @@ where HEIGHT and WIDTH are the normal height and width of the window. (if current-prefix-arg (window-parent) (window-main-window))) ;;;###autoload -(defun window-layout-rotate-anticlockwise (&optional window) - "Rotate window layout of WINDOW counterclockwise by 90 degrees. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to rotate the parent window of the -selected window." +(defun window-layout-rotate-clockwise (&optional window) + "Clockwise rotate layout of WINDOW's child windows. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, rotate clockwise +the layout of the child windows of the selected window's parent. Signal +an error if WINDOW is not a parent window + +Recursively rotate the entire layout of WINDOW's child windows clockwise +by 90 degrees. Do not change the selected window of WINDOW's frame. If +you want to rotate windows within their frame's layout, consider using +`rotate-windows' instead." (interactive (list (window--rotate-interactive-arg))) - (window--transpose window '(right . above) nil)) + (window--transpose (or window (window-main-window)) '(left . below) nil)) ;;;###autoload -(defun window-layout-rotate-clockwise (&optional window) - "Rotate window layout under WINDOW clockwise by 90 degrees. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to rotate the parent window of the -selected window." +(defun window-layout-rotate-anticlockwise (&optional window) + "Rotate layout of WINDOW's child windows counterclockwise by 90 degrees. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, rotate +counterclockwise the layout of the child windows of the selected +window's parent. Signal an error if WINDOW is not a parent window + +Recursively rotate the entire layout of WINDOW's child windows +counterclockwise by 90 degrees. Do not change the selected window of +WINDOW's frame. If you want to rotate windows within their frame's +layout, consider using `rotate-windows-back' instead." (interactive (list (window--rotate-interactive-arg))) - (window--transpose window '(left . below) nil)) + (window--transpose (or window (window-main-window)) '(right . above) nil)) ;;;###autoload (defun window-layout-flip-leftright (&optional window) - "Horizontally flip windows under WINDOW. - -Flip the window layout so that the window on the right becomes the -window on the left, and vice-versa. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to flip the parent window of the -selected window." + "Horizontally flip WINDOW's child windows. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, flip +horizontally the layout of the child windows of the selected window's +parent. Signal an error if WINDOW is not a parent window + +Recursively flip the layout of WINDOW's child windows so that a child +window on the right becomes a child window on the left and vice-versa." (interactive (list (window--rotate-interactive-arg))) - (window--transpose window '(below . left) t)) + (window--transpose (or window (window-main-window)) '(below . left) t)) ;;;###autoload (defun window-layout-flip-topdown (&optional window) - "Vertically flip windows under WINDOW. - -Flip the window layout so that the top window becomes the bottom window, -and vice-versa. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to flip the parent window of the -selected window." + "Vertically flip WINDOW's child windows. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, flip vertically +the layout of the child windows of the selected window's parent. Signal +an error if WINDOW is not a parent window + +Recursively flip the layout of WINDOW's child windows so that a child +window on the top becomes a child window on the bottom and vice-versa." (interactive (list (window--rotate-interactive-arg))) - (window--transpose window '(above . right) t)) + (window--transpose (or window (window-main-window)) '(above . right) t)) ;;;###autoload (defun window-layout-transpose (&optional window) - "Transpose windows under WINDOW. - -Reorganize the windows under WINDOW so that every horizontal split -becomes a vertical split, and vice versa. This is equivalent to -diagonally flipping. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to transpose the parent window of -the selected window." + "Transpose child windows of WINDOW. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, transpose the +layout of the child windows of the selected window's parent. Signal an +error if WINDOW is not a parent window + +Recursively reorganize WINDOW's child windows so that each horizontal +split becomes a vertical split and vice versa." (interactive (list (window--rotate-interactive-arg))) - (window--transpose window '(right . below) nil)) - -;;;###autoload -(defun rotate-windows-back (&optional window) - "Rotate windows under WINDOW backward in cyclic ordering. - -If WINDOW is nil, it defaults to the root window of the selected frame. + (window--transpose (or window (window-main-window)) '(right . below) nil)) -Interactively, a prefix argument says to rotate the parent window of the -selected window." - (interactive (list (window--rotate-interactive-arg))) - (rotate-windows window t)) +(defcustom rotate-windows-change-selected t + "Whether rotating windows will change the selected window. +If this is nil, `rotate-windows' and `rotate-windows-back' do not change +a frame's selected window. Otherwise, the new selected window will be +the window that appears at the location of the previously selected +window." + :type 'boolean + :group 'windows) ;;;###autoload (defun rotate-windows (&optional window reverse) - "Rotate windows under WINDOW in cyclic ordering. - -Optional argument REVERSE says to rotate windows backward, in reverse -cyclic order. - -If WINDOW is nil, it defaults to the root window of the selected frame. - -Interactively, a prefix argument says to rotate the parent window of the -selected window." + "Rotate child windows of WINDOW in cyclic ordering. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, rotate the child +windows of the selected window's parent. + +Optional argument REVERSE non-nil means to rotate windows backwards, in +reverse cyclic order. Signal an error if WINDOW is not a parent window, +all descendants of WINDOW are dedicated or some windows are of fixed +size or atomic. + +Rotating windows leaves the way a frame layout has been produced via +splitting and deleting windows unaltered. It only \"moves\" windows +within that layout such that the space formerly occupied by any window +is now occupied by the window preceding (following if REVERSE is +non-nil) it in that order. + +If you want to rotate the entire layout of windows consider using the +function `window-layout-rotate-clockwise' instead." (interactive (list (window--rotate-interactive-arg))) - (when (or (not window) (window-live-p window)) + (setq window (or window (window-main-window))) + (when (window-live-p window) (user-error "No windows to rotate")) (let* ((frame (window-frame window)) (selected-window (frame-selected-window window)) + (x-y (and rotate-windows-change-selected + (cons (1+ (window-pixel-left selected-window)) + (1+ (window-pixel-top selected-window))))) (win-tree (car (window-tree-normal-sizes window))) (winls (or (seq-filter @@ -175,11 +181,12 @@ selected window." (and (window-live-p win) (not (window-dedicated-p win)))) (flatten-list win-tree)) + ;; Do we really care - the window/buffer relationship + ;; should not be affected by rotating. (user-error "All windows are dedicated"))) (rotated-ls (if reverse (append (cdr winls) (list (car winls))) (append (last winls) winls))) - (other-window-arg (if reverse 1 -1)) (first-window (car rotated-ls)) (new-win-tree ;; Recursively process `win-tree' and construct a new tree @@ -193,14 +200,31 @@ selected window." (t tree))))) (when (or (seq-some #'window-atom-root winls) (seq-some #'window-fixed-size-p winls)) - (user-error "Cannot rotate windows due to fixed size or atom windows")) + (user-error "Cannot rotate windows due to fixed size or atomic windows")) (delete-other-windows-internal first-window window) (window--transpose-1 new-win-tree first-window '(below . right) t nil) - (set-frame-selected-window frame selected-window) - (when rotate-windows-change-selected - (other-window other-window-arg) - (while (not (memq (selected-window) winls)) - (other-window other-window-arg))))) + (when x-y + ;; Try to select the window at the position previously occupied by + ;; the selected window. + (let ((window-at-x-y (window-at-x-y (car x-y) (cdr x-y) frame t))) + (when window-at-x-y + (setq selected-window window-at-x-y)))) + + (set-frame-selected-window frame selected-window))) + +;;;###autoload +(defun rotate-windows-back (&optional window) + "Rotate child windows of WINDOW backwards in cyclic ordering. +WINDOW must be a parent window and defaults to the main window of the +selected frame. Interactively, with a prefix argument, rotate backwards +the child windows of the selected window's parent. Signal an error if +WINDOW is not a parent window, all descendants of WINDOW are dedicated +or some of them are of fixed size or atomic. + +If you want to rotate the entire layout of windows, consider using the +function `window-layout-rotate-anticlockwise' instead." + (interactive (list (window--rotate-interactive-arg))) + (rotate-windows window t)) (defun window--transpose (window conf no-resize) "Rearrange windows under WINDOW recursively. @@ -424,8 +448,7 @@ absolute value of ARG. Return the new frame." (let ((comb (window-get-split-combination main arg))) (window-state-put (window-state-get comb) (window-main-window (make-frame))) - (delete-window comb)) - )))) + (delete-window comb)))))) (provide 'window-x) ;;; window-x.el ends here diff --git a/src/window.c b/src/window.c index 0373af7ef27..e4b0e62f3a3 100644 --- a/src/window.c +++ b/src/window.c @@ -9454,7 +9454,10 @@ windows in the same combination. Other values are reserved for future use. A specific split operation may ignore the value of this variable if it -is affected by a non-nil value of `window-combination-limit'. */); +is affected by a non-nil value of `window-combination-limit'. If you +want to use a sequence of `split-window' calls to produce a specific, +predefined layout of windows on a frame, bind this variable temporarily +to nil. */); Vwindow_combination_resize = Qnil; DEFVAR_LISP ("window-combination-limit", Vwindow_combination_limit,