aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Rudalics2021-06-10 09:14:21 +0200
committerMartin Rudalics2021-06-10 09:14:21 +0200
commitb3dd0ce75ba9314eb7a682e5fcf8b4cfbc67655b (patch)
treeab7873f3a18ce3873200863e37fed9534558ce93
parentdd9385b404c28a155a91960a4f1c4c77fdc5413d (diff)
downloademacs-b3dd0ce75ba9314eb7a682e5fcf8b4cfbc67655b.tar.gz
emacs-b3dd0ce75ba9314eb7a682e5fcf8b4cfbc67655b.zip
Provide new option `delete-window-set-selected' (Bug#47300)
When `delete-window' deletes its frame's selected window, this new option allows to choose another window as replacement. * lisp/window.el (get-lru-window, get-mru-window) (get-largest-window): New optional argument NO-OTHER. (window-at-pos): New function. (delete-window-set-selected): New option. (delete-window): Handle `delete-window-set-selected'. * src/window.c (Fdelete_window_internal): Set the selected window of WINDOW's frame to the first window on that frame and let `delete-window' choose a more suitable window instead. * doc/lispref/windows.texi (Deleting Windows): Describe new option `delete-window-set-selected'. (Cyclic Window Ordering): Describe new NO-OTHER argument for `get-lru-window', `get-mru-window' and `get-largest-window'. * etc/NEWS: Mention `delete-window-set-selected' and the NO-OTHER argument.
-rw-r--r--doc/lispref/windows.texi47
-rw-r--r--etc/NEWS11
-rw-r--r--lisp/window.el157
-rw-r--r--src/window.c25
4 files changed, 171 insertions, 69 deletions
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 44656c057a3..bcb492d68f7 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1318,6 +1318,23 @@ lieu of the usual action of @code{delete-window}. @xref{Window
1318Parameters}. 1318Parameters}.
1319@end deffn 1319@end deffn
1320 1320
1321When @code{delete-window} deletes the selected window of its frame, it
1322has to make another window the new selected window of that frame. The
1323following option allows to choose which window gets selected instead.
1324
1325@defopt delete-window-set-selected
1326This option allows to specify which window should become a frame's
1327selected window after @code{delete-window} has deleted the previously
1328selected one.
1329
1330Possible choices are @code{mru} (the default) to select the most
1331recently used window on that frame and @code{pos} to choose the window
1332at the position of point of the previously selected window. If this
1333option is @code{nil}, it means to choose the frame's first window
1334instead. Note that a window with a non-@code{nil}
1335@code{no-other-window} parameter is never chosen.
1336@end defopt
1337
1321@deffn Command delete-other-windows &optional window 1338@deffn Command delete-other-windows &optional window
1322This function makes @var{window} fill its frame, deleting other 1339This function makes @var{window} fill its frame, deleting other
1323windows as necessary. If @var{window} is omitted or @code{nil}, it 1340windows as necessary. If @var{window} is omitted or @code{nil}, it
@@ -2007,7 +2024,7 @@ meaning as for @code{next-window}.
2007criterion, without selecting it: 2024criterion, without selecting it:
2008 2025
2009@cindex least recently used window 2026@cindex least recently used window
2010@defun get-lru-window &optional all-frames dedicated not-selected 2027@defun get-lru-window &optional all-frames dedicated not-selected no-other
2011This function returns a live window which is heuristically the least 2028This function returns a live window which is heuristically the least
2012recently used. The optional argument @var{all-frames} has 2029recently used. The optional argument @var{all-frames} has
2013the same meaning as in @code{next-window}. 2030the same meaning as in @code{next-window}.
@@ -2018,33 +2035,25 @@ window (@pxref{Dedicated Windows}) is never a candidate unless the
2018optional argument @var{dedicated} is non-@code{nil}. The selected 2035optional argument @var{dedicated} is non-@code{nil}. The selected
2019window is never returned, unless it is the only candidate. However, if 2036window is never returned, unless it is the only candidate. However, if
2020the optional argument @var{not-selected} is non-@code{nil}, this 2037the optional argument @var{not-selected} is non-@code{nil}, this
2021function returns @code{nil} in that case. 2038function returns @code{nil} in that case. The optional argument
2039@var{no-other}, if non-@code{nil}, means to never return a window whose
2040@code{no-other-window} parameter is non-@code{nil}.
2022@end defun 2041@end defun
2023 2042
2024@cindex most recently used window 2043@cindex most recently used window
2025@defun get-mru-window &optional all-frames dedicated not-selected 2044@defun get-mru-window &optional all-frames dedicated not-selected no-other
2026This function is like @code{get-lru-window}, but it returns the most 2045This function is like @code{get-lru-window}, but it returns the most
2027recently used window instead. The meaning of the arguments is the 2046recently used window instead. The meaning of the arguments is the
2028same as described for @code{get-lru-window}. 2047same as for @code{get-lru-window}.
2029@end defun 2048@end defun
2030 2049
2031@cindex largest window 2050@cindex largest window
2032@defun get-largest-window &optional all-frames dedicated not-selected 2051@defun get-largest-window &optional all-frames dedicated not-selected no-other
2033This function returns the window with the largest area (height times 2052This function returns the window with the largest area (height times
2034width). The optional argument @var{all-frames} specifies the windows to 2053width). If there are two candidate windows of the same size, it prefers
2035search, and has the same meaning as in @code{next-window}. 2054the one that comes first in the cyclic ordering of windows, starting
2036 2055from the selected window. The meaning of the arguments is the same as
2037A minibuffer window is never a candidate. A dedicated window 2056for @code{get-lru-window}.
2038(@pxref{Dedicated Windows}) is never a candidate unless the optional
2039argument @var{dedicated} is non-@code{nil}. The selected window is not
2040a candidate if the optional argument @var{not-selected} is
2041non-@code{nil}. If the optional argument @var{not-selected} is
2042non-@code{nil} and the selected window is the only candidate, this
2043function returns @code{nil}.
2044
2045If there are two candidate windows of the same size, this function
2046prefers the one that comes first in the cyclic ordering of windows,
2047starting from the selected window.
2048@end defun 2057@end defun
2049 2058
2050@cindex window that satisfies a predicate 2059@cindex window that satisfies a predicate
diff --git a/etc/NEWS b/etc/NEWS
index 3075fa0cd4e..06c6efd995a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -605,6 +605,17 @@ These options include 'windmove-default-keybindings',
605** Windows 605** Windows
606 606
607+++ 607+++
608*** New option 'delete-window-set-selected'.
609This allows to choose a frame's selected window after deleting the
610previously selected one.
611
612+++
613*** New argument NO-OTHER for some window functions.
614'get-lru-window', ‘get-mru-window’ and 'get-largest-window' now accept a
615new optional argument NO-OTHER which if non-nil avoids returning a
616window whose 'no-other-window' parameter is non-nil.
617
618+++
608*** New 'display-buffer' function 'display-buffer-use-least-recent-window'. 619*** New 'display-buffer' function 'display-buffer-use-least-recent-window'.
609This is like 'display-buffer-use-some-window', but won't reuse the 620This is like 'display-buffer-use-some-window', but won't reuse the
610current window, and when called repeatedly will try not to reuse a 621current window, and when called repeatedly will try not to reuse a
diff --git a/lisp/window.el b/lisp/window.el
index fd1c617d6be..e7551f30c32 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -2499,14 +2499,16 @@ and no others."
2499 2499
2500(defalias 'some-window 'get-window-with-predicate) 2500(defalias 'some-window 'get-window-with-predicate)
2501 2501
2502(defun get-lru-window (&optional all-frames dedicated not-selected) 2502(defun get-lru-window (&optional all-frames dedicated not-selected no-other)
2503 "Return the least recently used window on frames specified by ALL-FRAMES. 2503 "Return the least recently used window on frames specified by ALL-FRAMES.
2504Return a full-width window if possible. A minibuffer window is 2504Return a full-width window if possible. A minibuffer window is
2505never a candidate. A dedicated window is never a candidate 2505never a candidate. A dedicated window is never a candidate
2506unless DEDICATED is non-nil, so if all windows are dedicated, the 2506unless DEDICATED is non-nil, so if all windows are dedicated, the
2507value is nil. Avoid returning the selected window if possible. 2507value is nil. Avoid returning the selected window if possible.
2508Optional argument NOT-SELECTED non-nil means never return the 2508Optional argument NOT-SELECTED non-nil means never return the
2509selected window. 2509selected window. Optional argument NO-OTHER non-nil means to
2510never return a window whose 'no-other-window' parameter is
2511non-nil.
2510 2512
2511The following non-nil values of the optional argument ALL-FRAMES 2513The following non-nil values of the optional argument ALL-FRAMES
2512have special meanings: 2514have special meanings:
@@ -2526,7 +2528,9 @@ selected frame and no others."
2526 (let (best-window best-time second-best-window second-best-time time) 2528 (let (best-window best-time second-best-window second-best-time time)
2527 (dolist (window (window-list-1 nil 'nomini all-frames)) 2529 (dolist (window (window-list-1 nil 'nomini all-frames))
2528 (when (and (or dedicated (not (window-dedicated-p window))) 2530 (when (and (or dedicated (not (window-dedicated-p window)))
2529 (or (not not-selected) (not (eq window (selected-window))))) 2531 (or (not not-selected) (not (eq window (selected-window))))
2532 (or (not no-other)
2533 (not (window-parameter window 'no-other-window))))
2530 (setq time (window-use-time window)) 2534 (setq time (window-use-time window))
2531 (if (or (eq window (selected-window)) 2535 (if (or (eq window (selected-window))
2532 (not (window-full-width-p window))) 2536 (not (window-full-width-p window)))
@@ -2538,12 +2542,14 @@ selected frame and no others."
2538 (setq best-window window))))) 2542 (setq best-window window)))))
2539 (or best-window second-best-window))) 2543 (or best-window second-best-window)))
2540 2544
2541(defun get-mru-window (&optional all-frames dedicated not-selected) 2545(defun get-mru-window (&optional all-frames dedicated not-selected no-other)
2542 "Return the most recently used window on frames specified by ALL-FRAMES. 2546 "Return the most recently used window on frames specified by ALL-FRAMES.
2543A minibuffer window is never a candidate. A dedicated window is 2547A minibuffer window is never a candidate. A dedicated window is
2544never a candidate unless DEDICATED is non-nil, so if all windows 2548never a candidate unless DEDICATED is non-nil, so if all windows
2545are dedicated, the value is nil. Optional argument NOT-SELECTED 2549are dedicated, the value is nil. Optional argument NOT-SELECTED
2546non-nil means never return the selected window. 2550non-nil means never return the selected window. Optional
2551argument NO-OTHER non-nil means to never return a window whose
2552'no-other-window' parameter is non-nil.
2547 2553
2548The following non-nil values of the optional argument ALL-FRAMES 2554The following non-nil values of the optional argument ALL-FRAMES
2549have special meanings: 2555have special meanings:
@@ -2565,17 +2571,21 @@ selected frame and no others."
2565 (setq time (window-use-time window)) 2571 (setq time (window-use-time window))
2566 (when (and (or dedicated (not (window-dedicated-p window))) 2572 (when (and (or dedicated (not (window-dedicated-p window)))
2567 (or (not not-selected) (not (eq window (selected-window)))) 2573 (or (not not-selected) (not (eq window (selected-window))))
2568 (or (not best-time) (> time best-time))) 2574 (or (not no-other)
2575 (not (window-parameter window 'no-other-window)))
2576 (or (not best-time) (> time best-time)))
2569 (setq best-time time) 2577 (setq best-time time)
2570 (setq best-window window))) 2578 (setq best-window window)))
2571 best-window)) 2579 best-window))
2572 2580
2573(defun get-largest-window (&optional all-frames dedicated not-selected) 2581(defun get-largest-window (&optional all-frames dedicated not-selected no-other)
2574 "Return the largest window on frames specified by ALL-FRAMES. 2582 "Return the largest window on frames specified by ALL-FRAMES.
2575A minibuffer window is never a candidate. A dedicated window is 2583A minibuffer window is never a candidate. A dedicated window is
2576never a candidate unless DEDICATED is non-nil, so if all windows 2584never a candidate unless DEDICATED is non-nil, so if all windows
2577are dedicated, the value is nil. Optional argument NOT-SELECTED 2585are dedicated, the value is nil. Optional argument NOT-SELECTED
2578non-nil means never return the selected window. 2586non-nil means never return the selected window. Optional
2587argument NO-OTHER non-nil means to never return a window whose
2588'no-other-window' parameter is non-nil.
2579 2589
2580The following non-nil values of the optional argument ALL-FRAMES 2590The following non-nil values of the optional argument ALL-FRAMES
2581have special meanings: 2591have special meanings:
@@ -2596,7 +2606,9 @@ selected frame and no others."
2596 best-window size) 2606 best-window size)
2597 (dolist (window (window-list-1 nil 'nomini all-frames)) 2607 (dolist (window (window-list-1 nil 'nomini all-frames))
2598 (when (and (or dedicated (not (window-dedicated-p window))) 2608 (when (and (or dedicated (not (window-dedicated-p window)))
2599 (or (not not-selected) (not (eq window (selected-window))))) 2609 (or (not not-selected) (not (eq window (selected-window))))
2610 (or (not no-other)
2611 (not (window-parameter window 'no-other-window))))
2600 (setq size (* (window-pixel-height window) 2612 (setq size (* (window-pixel-height window)
2601 (window-pixel-width window))) 2613 (window-pixel-width window)))
2602 (when (> size best-size) 2614 (when (> size best-size)
@@ -4130,18 +4142,53 @@ frame can be safely deleted."
4130 ;; of its frame. 4142 ;; of its frame.
4131 t)))) 4143 t))))
4132 4144
4133(defun window--in-subtree-p (window root) 4145(defun window-at-pos (x y &optional frame no-other)
4134 "Return t if WINDOW is either ROOT or a member of ROOT's subtree." 4146 "Return live window at coordinates X, Y on specified FRAME.
4135 (or (eq window root) 4147X and Y are counted in pixels from an origin at 0, 0 of FRAME's
4136 (let ((parent (window-parent window))) 4148native frame. A coordinate on an edge shared by two windows is
4137 (catch 'done 4149attributed to the window on the right (or below). Return nil if
4138 (while parent 4150no such window can be found.
4139 (if (eq parent root) 4151
4140 (throw 'done t) 4152Optional argument FRAME must specify a live frame and defaults to
4141 (setq parent (window-parent parent)))))))) 4153the selected one. Optional argument NO-OTHER non-nil means to
4154not return a window with a non-nil 'no-other-window' parameter."
4155 (setq frame (window-normalize-frame frame))
4156 (let* ((root-edges (window-edges (frame-root-window frame) nil nil t))
4157 (root-left (nth 2 root-edges))
4158 (root-bottom (nth 3 root-edges)))
4159 (catch 'window
4160 (walk-window-tree
4161 (lambda (window)
4162 (let ((edges (window-edges window nil nil t)))
4163 (when (and (>= x (nth 0 edges))
4164 (or (< x (nth 2 edges)) (= x root-left))
4165 (>= y (nth 1 edges))
4166 (or (< y (nth 3 edges)) (= y root-bottom)))
4167 (if (and no-other (window-parameter window 'no-other-window))
4168 (throw 'window nil)
4169 (throw 'window window)))))
4170 frame))))
4171
4172(defcustom delete-window-set-selected 'mru
4173 "How to choose a frame's selected window after window deletion.
4174When a frame's selected window gets deleted, Emacs has to choose
4175another live window on that frame to serve as its selected
4176window. This option allows to control which window gets selected
4177instead.
4178
4179The possible choices are 'mru' (the default) to select the most
4180recently used window on that frame and 'pos' to choose the window
4181at the position of point of the previously selected window. If
4182this is nil, choose the frame's first window instead. A window
4183with a non-nil 'no-other-window' parameter is never chosen."
4184 :type '(choice (const :tag "Most recently used" mru)
4185 (const :tag "At position of deleted" pos)
4186 (const :tag "Frame's first " nil))
4187 :group 'windows
4188 :version "28.1")
4142 4189
4143(defun delete-window (&optional window) 4190(defun delete-window (&optional window)
4144 "Delete WINDOW. 4191 "Delete specified WINDOW.
4145WINDOW must be a valid window and defaults to the selected one. 4192WINDOW must be a valid window and defaults to the selected one.
4146Return nil. 4193Return nil.
4147 4194
@@ -4156,7 +4203,11 @@ Otherwise, if WINDOW is part of an atomic window, call
4156`delete-window' with the root of the atomic window as its 4203`delete-window' with the root of the atomic window as its
4157argument. Signal an error if WINDOW is either the only window on 4204argument. Signal an error if WINDOW is either the only window on
4158its frame, the last non-side window, or part of an atomic window 4205its frame, the last non-side window, or part of an atomic window
4159that is its frame's root window." 4206that is its frame's root window.
4207
4208If WINDOW is the selected window on its frame, choose some other
4209window as that frame's selected window according to the value of
4210the option `delete-window-set-selected'."
4160 (interactive) 4211 (interactive)
4161 (setq window (window-normalize-window window)) 4212 (setq window (window-normalize-window window))
4162 (let* ((frame (window-frame window)) 4213 (let* ((frame (window-frame window))
@@ -4191,11 +4242,11 @@ that is its frame's root window."
4191 (window-combination-resize 4242 (window-combination-resize
4192 (or window-combination-resize 4243 (or window-combination-resize
4193 (window-parameter parent 'window-side))) 4244 (window-parameter parent 'window-side)))
4194 (frame-selected 4245 (frame-selected-window (frame-selected-window frame))
4195 (window--in-subtree-p (frame-selected-window frame) window))
4196 ;; Emacs 23 preferably gives WINDOW's space to its left 4246 ;; Emacs 23 preferably gives WINDOW's space to its left
4197 ;; sibling. 4247 ;; sibling.
4198 (sibling (or (window-left window) (window-right window)))) 4248 (sibling (or (window-left window) (window-right window)))
4249 frame-selected-window-edges frame-selected-window-pos)
4199 (window--resize-reset frame horizontal) 4250 (window--resize-reset frame horizontal)
4200 (cond 4251 (cond
4201 ((and (not (eq window-combination-resize t)) 4252 ((and (not (eq window-combination-resize t))
@@ -4211,15 +4262,63 @@ that is its frame's root window."
4211 (t 4262 (t
4212 ;; Can't do without resizing fixed-size windows. 4263 ;; Can't do without resizing fixed-size windows.
4213 (window--resize-siblings window (- size) horizontal t))) 4264 (window--resize-siblings window (- size) horizontal t)))
4265
4266 (when (eq delete-window-set-selected 'pos)
4267 ;; Remember edges and position of point of the selected window
4268 ;; of WINDOW'S frame.
4269 (setq frame-selected-window-edges
4270 (window-edges frame-selected-window nil nil t))
4271 (setq frame-selected-window-pos
4272 (nth 2 (posn-at-point nil frame-selected-window))))
4273
4214 ;; Actually delete WINDOW. 4274 ;; Actually delete WINDOW.
4215 (delete-window-internal window) 4275 (delete-window-internal window)
4216 (window--pixel-to-total frame horizontal) 4276 (window--pixel-to-total frame horizontal)
4217 (when (and frame-selected 4277
4218 (window-parameter 4278 ;; If we deleted the selected window of WINDOW's frame, choose
4219 (frame-selected-window frame) 'no-other-window)) 4279 ;; another one based on `delete-window-set-selected'. Note
4220 ;; `delete-window-internal' has selected a window that should 4280 ;; that both `window-at-pos' and `get-mru-window' may fail to
4221 ;; not be selected, fix this here. 4281 ;; produce a suitable window in which case we will fall back on
4222 (other-window -1 frame)) 4282 ;; its frame's first window, chosen by `delete-window-internal'.
4283 (cond
4284 ((window-live-p frame-selected-window))
4285 ((and frame-selected-window-pos
4286 ;; We have a recorded position of point of the previously
4287 ;; selected window. Try to find the window that is now
4288 ;; at that position.
4289 (let ((new-frame-selected-window
4290 (window-at-pos
4291 (+ (nth 0 frame-selected-window-edges)
4292 (car frame-selected-window-pos))
4293 (+ (nth 1 frame-selected-window-edges)
4294 (cdr frame-selected-window-pos))
4295 frame t)))
4296 (and new-frame-selected-window
4297 ;; Select window at WINDOW's position at point.
4298 (set-frame-selected-window
4299 frame new-frame-selected-window)))))
4300 ((and (eq delete-window-set-selected 'mru)
4301 ;; Try to use the most recently used window.
4302 (let ((mru-window (get-mru-window frame nil nil t)))
4303 (and mru-window
4304 (set-frame-selected-window frame mru-window)))))
4305 ((and (window-parameter
4306 (frame-selected-window frame) 'no-other-window)
4307 ;; If `delete-window-internal' selected a window with a
4308 ;; non-nil 'no-other-window' parameter as its frame's
4309 ;; selected window, try to choose another one.
4310 (catch 'found
4311 (walk-window-tree
4312 (lambda (other)
4313 (unless (window-parameter other 'no-other-window)
4314 (set-frame-selected-window frame other)
4315 (throw 'found t)))
4316 frame))))
4317 (t
4318 ;; Record the window chosen by `delete-window-internal'.
4319 (set-frame-selected-window
4320 frame (frame-selected-window frame))))
4321
4223 (window--check frame) 4322 (window--check frame)
4224 ;; Always return nil. 4323 ;; Always return nil.
4225 nil)))) 4324 nil))))
diff --git a/src/window.c b/src/window.c
index 2d98ae5f156..db324effcce 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5148,15 +5148,13 @@ Signal an error when WINDOW is the only window on its frame. */)
5148 adjust_frame_glyphs (f); 5148 adjust_frame_glyphs (f);
5149 5149
5150 if (!WINDOW_LIVE_P (FRAME_SELECTED_WINDOW (f))) 5150 if (!WINDOW_LIVE_P (FRAME_SELECTED_WINDOW (f)))
5151 /* We deleted the frame's selected window. */ 5151 /* We apparently deleted the frame's selected window; use the
5152 frame's first window as substitute but don't record it yet.
5153 `delete-window' may have something better up its sleeves. */
5152 { 5154 {
5153 /* Use the frame's first window as fallback ... */ 5155 /* Use the frame's first window as fallback ... */
5154 Lisp_Object new_selected_window = Fframe_first_window (frame); 5156 Lisp_Object new_selected_window = Fframe_first_window (frame);
5155 /* ... but preferably use its most recently used window. */
5156 Lisp_Object mru_window;
5157 5157
5158 /* `get-mru-window' might fail for some reason so play it safe
5159 - promote the first window _without recording it_ first. */
5160 if (EQ (FRAME_SELECTED_WINDOW (f), selected_window)) 5158 if (EQ (FRAME_SELECTED_WINDOW (f), selected_window))
5161 Fselect_window (new_selected_window, Qt); 5159 Fselect_window (new_selected_window, Qt);
5162 else 5160 else
@@ -5164,24 +5162,9 @@ Signal an error when WINDOW is the only window on its frame. */)
5164 last selected window on F was an active minibuffer, we 5162 last selected window on F was an active minibuffer, we
5165 want to return to it on a later Fselect_frame. */ 5163 want to return to it on a later Fselect_frame. */
5166 fset_selected_window (f, new_selected_window); 5164 fset_selected_window (f, new_selected_window);
5167
5168 unblock_input ();
5169
5170 /* Now look whether `get-mru-window' gets us something. */
5171 mru_window = call1 (Qget_mru_window, frame);
5172 if (WINDOW_LIVE_P (mru_window)
5173 && EQ (XWINDOW (mru_window)->frame, frame))
5174 new_selected_window = mru_window;
5175
5176 /* If all ended up well, we now promote the mru window. */
5177 if (EQ (FRAME_SELECTED_WINDOW (f), selected_window))
5178 Fselect_window (new_selected_window, Qnil);
5179 else
5180 fset_selected_window (f, new_selected_window);
5181 } 5165 }
5182 else
5183 unblock_input ();
5184 5166
5167 unblock_input ();
5185 FRAME_WINDOW_CHANGE (f) = true; 5168 FRAME_WINDOW_CHANGE (f) = true;
5186 } 5169 }
5187 else 5170 else