mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-11 16:08:13 +00:00
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.
This commit is contained in:
parent
dd9385b404
commit
b3dd0ce75b
@ -1318,6 +1318,23 @@ lieu of the usual action of @code{delete-window}. @xref{Window
|
||||
Parameters}.
|
||||
@end deffn
|
||||
|
||||
When @code{delete-window} deletes the selected window of its frame, it
|
||||
has to make another window the new selected window of that frame. The
|
||||
following option allows to choose which window gets selected instead.
|
||||
|
||||
@defopt delete-window-set-selected
|
||||
This option allows to specify which window should become a frame's
|
||||
selected window after @code{delete-window} has deleted the previously
|
||||
selected one.
|
||||
|
||||
Possible choices are @code{mru} (the default) to select the most
|
||||
recently used window on that frame and @code{pos} to choose the window
|
||||
at the position of point of the previously selected window. If this
|
||||
option is @code{nil}, it means to choose the frame's first window
|
||||
instead. Note that a window with a non-@code{nil}
|
||||
@code{no-other-window} parameter is never chosen.
|
||||
@end defopt
|
||||
|
||||
@deffn Command delete-other-windows &optional window
|
||||
This function makes @var{window} fill its frame, deleting other
|
||||
windows as necessary. If @var{window} is omitted or @code{nil}, it
|
||||
@ -2007,7 +2024,7 @@ meaning as for @code{next-window}.
|
||||
criterion, without selecting it:
|
||||
|
||||
@cindex least recently used window
|
||||
@defun get-lru-window &optional all-frames dedicated not-selected
|
||||
@defun get-lru-window &optional all-frames dedicated not-selected no-other
|
||||
This function returns a live window which is heuristically the least
|
||||
recently used. The optional argument @var{all-frames} has
|
||||
the same meaning as in @code{next-window}.
|
||||
@ -2018,33 +2035,25 @@ window (@pxref{Dedicated Windows}) is never a candidate unless the
|
||||
optional argument @var{dedicated} is non-@code{nil}. The selected
|
||||
window is never returned, unless it is the only candidate. However, if
|
||||
the optional argument @var{not-selected} is non-@code{nil}, this
|
||||
function returns @code{nil} in that case.
|
||||
function returns @code{nil} in that case. The optional argument
|
||||
@var{no-other}, if non-@code{nil}, means to never return a window whose
|
||||
@code{no-other-window} parameter is non-@code{nil}.
|
||||
@end defun
|
||||
|
||||
@cindex most recently used window
|
||||
@defun get-mru-window &optional all-frames dedicated not-selected
|
||||
@defun get-mru-window &optional all-frames dedicated not-selected no-other
|
||||
This function is like @code{get-lru-window}, but it returns the most
|
||||
recently used window instead. The meaning of the arguments is the
|
||||
same as described for @code{get-lru-window}.
|
||||
same as for @code{get-lru-window}.
|
||||
@end defun
|
||||
|
||||
@cindex largest window
|
||||
@defun get-largest-window &optional all-frames dedicated not-selected
|
||||
@defun get-largest-window &optional all-frames dedicated not-selected no-other
|
||||
This function returns the window with the largest area (height times
|
||||
width). The optional argument @var{all-frames} specifies the windows to
|
||||
search, and has the same meaning as in @code{next-window}.
|
||||
|
||||
A minibuffer window is never a candidate. A dedicated window
|
||||
(@pxref{Dedicated Windows}) is never a candidate unless the optional
|
||||
argument @var{dedicated} is non-@code{nil}. The selected window is not
|
||||
a candidate if the optional argument @var{not-selected} is
|
||||
non-@code{nil}. If the optional argument @var{not-selected} is
|
||||
non-@code{nil} and the selected window is the only candidate, this
|
||||
function returns @code{nil}.
|
||||
|
||||
If there are two candidate windows of the same size, this function
|
||||
prefers the one that comes first in the cyclic ordering of windows,
|
||||
starting from the selected window.
|
||||
width). If there are two candidate windows of the same size, it prefers
|
||||
the one that comes first in the cyclic ordering of windows, starting
|
||||
from the selected window. The meaning of the arguments is the same as
|
||||
for @code{get-lru-window}.
|
||||
@end defun
|
||||
|
||||
@cindex window that satisfies a predicate
|
||||
|
11
etc/NEWS
11
etc/NEWS
@ -604,6 +604,17 @@ These options include 'windmove-default-keybindings',
|
||||
|
||||
** Windows
|
||||
|
||||
+++
|
||||
*** New option 'delete-window-set-selected'.
|
||||
This allows to choose a frame's selected window after deleting the
|
||||
previously selected one.
|
||||
|
||||
+++
|
||||
*** New argument NO-OTHER for some window functions.
|
||||
'get-lru-window', ‘get-mru-window’ and 'get-largest-window' now accept a
|
||||
new optional argument NO-OTHER which if non-nil avoids returning a
|
||||
window whose 'no-other-window' parameter is non-nil.
|
||||
|
||||
+++
|
||||
*** New 'display-buffer' function 'display-buffer-use-least-recent-window'.
|
||||
This is like 'display-buffer-use-some-window', but won't reuse the
|
||||
|
157
lisp/window.el
157
lisp/window.el
@ -2499,14 +2499,16 @@ and no others."
|
||||
|
||||
(defalias 'some-window 'get-window-with-predicate)
|
||||
|
||||
(defun get-lru-window (&optional all-frames dedicated not-selected)
|
||||
(defun get-lru-window (&optional all-frames dedicated not-selected no-other)
|
||||
"Return the least recently used window on frames specified by ALL-FRAMES.
|
||||
Return a full-width window if possible. A minibuffer window is
|
||||
never a candidate. A dedicated window is never a candidate
|
||||
unless DEDICATED is non-nil, so if all windows are dedicated, the
|
||||
value is nil. Avoid returning the selected window if possible.
|
||||
Optional argument NOT-SELECTED non-nil means never return the
|
||||
selected window.
|
||||
selected window. Optional argument NO-OTHER non-nil means to
|
||||
never return a window whose 'no-other-window' parameter is
|
||||
non-nil.
|
||||
|
||||
The following non-nil values of the optional argument ALL-FRAMES
|
||||
have special meanings:
|
||||
@ -2526,7 +2528,9 @@ selected frame and no others."
|
||||
(let (best-window best-time second-best-window second-best-time time)
|
||||
(dolist (window (window-list-1 nil 'nomini all-frames))
|
||||
(when (and (or dedicated (not (window-dedicated-p window)))
|
||||
(or (not not-selected) (not (eq window (selected-window)))))
|
||||
(or (not not-selected) (not (eq window (selected-window))))
|
||||
(or (not no-other)
|
||||
(not (window-parameter window 'no-other-window))))
|
||||
(setq time (window-use-time window))
|
||||
(if (or (eq window (selected-window))
|
||||
(not (window-full-width-p window)))
|
||||
@ -2538,12 +2542,14 @@ selected frame and no others."
|
||||
(setq best-window window)))))
|
||||
(or best-window second-best-window)))
|
||||
|
||||
(defun get-mru-window (&optional all-frames dedicated not-selected)
|
||||
(defun get-mru-window (&optional all-frames dedicated not-selected no-other)
|
||||
"Return the most recently used window on frames specified by ALL-FRAMES.
|
||||
A minibuffer window is never a candidate. A dedicated window is
|
||||
never a candidate unless DEDICATED is non-nil, so if all windows
|
||||
are dedicated, the value is nil. Optional argument NOT-SELECTED
|
||||
non-nil means never return the selected window.
|
||||
non-nil means never return the selected window. Optional
|
||||
argument NO-OTHER non-nil means to never return a window whose
|
||||
'no-other-window' parameter is non-nil.
|
||||
|
||||
The following non-nil values of the optional argument ALL-FRAMES
|
||||
have special meanings:
|
||||
@ -2565,17 +2571,21 @@ selected frame and no others."
|
||||
(setq time (window-use-time window))
|
||||
(when (and (or dedicated (not (window-dedicated-p window)))
|
||||
(or (not not-selected) (not (eq window (selected-window))))
|
||||
(or (not best-time) (> time best-time)))
|
||||
(or (not no-other)
|
||||
(not (window-parameter window 'no-other-window)))
|
||||
(or (not best-time) (> time best-time)))
|
||||
(setq best-time time)
|
||||
(setq best-window window)))
|
||||
best-window))
|
||||
|
||||
(defun get-largest-window (&optional all-frames dedicated not-selected)
|
||||
(defun get-largest-window (&optional all-frames dedicated not-selected no-other)
|
||||
"Return the largest window on frames specified by ALL-FRAMES.
|
||||
A minibuffer window is never a candidate. A dedicated window is
|
||||
never a candidate unless DEDICATED is non-nil, so if all windows
|
||||
are dedicated, the value is nil. Optional argument NOT-SELECTED
|
||||
non-nil means never return the selected window.
|
||||
non-nil means never return the selected window. Optional
|
||||
argument NO-OTHER non-nil means to never return a window whose
|
||||
'no-other-window' parameter is non-nil.
|
||||
|
||||
The following non-nil values of the optional argument ALL-FRAMES
|
||||
have special meanings:
|
||||
@ -2596,7 +2606,9 @@ selected frame and no others."
|
||||
best-window size)
|
||||
(dolist (window (window-list-1 nil 'nomini all-frames))
|
||||
(when (and (or dedicated (not (window-dedicated-p window)))
|
||||
(or (not not-selected) (not (eq window (selected-window)))))
|
||||
(or (not not-selected) (not (eq window (selected-window))))
|
||||
(or (not no-other)
|
||||
(not (window-parameter window 'no-other-window))))
|
||||
(setq size (* (window-pixel-height window)
|
||||
(window-pixel-width window)))
|
||||
(when (> size best-size)
|
||||
@ -4130,18 +4142,53 @@ frame can be safely deleted."
|
||||
;; of its frame.
|
||||
t))))
|
||||
|
||||
(defun window--in-subtree-p (window root)
|
||||
"Return t if WINDOW is either ROOT or a member of ROOT's subtree."
|
||||
(or (eq window root)
|
||||
(let ((parent (window-parent window)))
|
||||
(catch 'done
|
||||
(while parent
|
||||
(if (eq parent root)
|
||||
(throw 'done t)
|
||||
(setq parent (window-parent parent))))))))
|
||||
(defun window-at-pos (x y &optional frame no-other)
|
||||
"Return live window at coordinates X, Y on specified FRAME.
|
||||
X and Y are counted in pixels from an origin at 0, 0 of FRAME's
|
||||
native frame. A coordinate on an edge shared by two windows is
|
||||
attributed to the window on the right (or below). Return nil if
|
||||
no such window can be found.
|
||||
|
||||
Optional argument FRAME must specify a live frame and defaults to
|
||||
the selected one. Optional argument NO-OTHER non-nil means to
|
||||
not return a window with a non-nil 'no-other-window' parameter."
|
||||
(setq frame (window-normalize-frame frame))
|
||||
(let* ((root-edges (window-edges (frame-root-window frame) nil nil t))
|
||||
(root-left (nth 2 root-edges))
|
||||
(root-bottom (nth 3 root-edges)))
|
||||
(catch 'window
|
||||
(walk-window-tree
|
||||
(lambda (window)
|
||||
(let ((edges (window-edges window nil nil t)))
|
||||
(when (and (>= x (nth 0 edges))
|
||||
(or (< x (nth 2 edges)) (= x root-left))
|
||||
(>= y (nth 1 edges))
|
||||
(or (< y (nth 3 edges)) (= y root-bottom)))
|
||||
(if (and no-other (window-parameter window 'no-other-window))
|
||||
(throw 'window nil)
|
||||
(throw 'window window)))))
|
||||
frame))))
|
||||
|
||||
(defcustom delete-window-set-selected 'mru
|
||||
"How to choose a frame's selected window after window deletion.
|
||||
When a frame's selected window gets deleted, Emacs has to choose
|
||||
another live window on that frame to serve as its selected
|
||||
window. This option allows to control which window gets selected
|
||||
instead.
|
||||
|
||||
The possible choices are 'mru' (the default) to select the most
|
||||
recently used window on that frame and 'pos' to choose the window
|
||||
at the position of point of the previously selected window. If
|
||||
this is nil, choose the frame's first window instead. A window
|
||||
with a non-nil 'no-other-window' parameter is never chosen."
|
||||
:type '(choice (const :tag "Most recently used" mru)
|
||||
(const :tag "At position of deleted" pos)
|
||||
(const :tag "Frame's first " nil))
|
||||
:group 'windows
|
||||
:version "28.1")
|
||||
|
||||
(defun delete-window (&optional window)
|
||||
"Delete WINDOW.
|
||||
"Delete specified WINDOW.
|
||||
WINDOW must be a valid window and defaults to the selected one.
|
||||
Return nil.
|
||||
|
||||
@ -4156,7 +4203,11 @@ Otherwise, if WINDOW is part of an atomic window, call
|
||||
`delete-window' with the root of the atomic window as its
|
||||
argument. Signal an error if WINDOW is either the only window on
|
||||
its frame, the last non-side window, or part of an atomic window
|
||||
that is its frame's root window."
|
||||
that is its frame's root window.
|
||||
|
||||
If WINDOW is the selected window on its frame, choose some other
|
||||
window as that frame's selected window according to the value of
|
||||
the option `delete-window-set-selected'."
|
||||
(interactive)
|
||||
(setq window (window-normalize-window window))
|
||||
(let* ((frame (window-frame window))
|
||||
@ -4191,11 +4242,11 @@ that is its frame's root window."
|
||||
(window-combination-resize
|
||||
(or window-combination-resize
|
||||
(window-parameter parent 'window-side)))
|
||||
(frame-selected
|
||||
(window--in-subtree-p (frame-selected-window frame) window))
|
||||
(frame-selected-window (frame-selected-window frame))
|
||||
;; Emacs 23 preferably gives WINDOW's space to its left
|
||||
;; sibling.
|
||||
(sibling (or (window-left window) (window-right window))))
|
||||
(sibling (or (window-left window) (window-right window)))
|
||||
frame-selected-window-edges frame-selected-window-pos)
|
||||
(window--resize-reset frame horizontal)
|
||||
(cond
|
||||
((and (not (eq window-combination-resize t))
|
||||
@ -4211,15 +4262,63 @@ that is its frame's root window."
|
||||
(t
|
||||
;; Can't do without resizing fixed-size windows.
|
||||
(window--resize-siblings window (- size) horizontal t)))
|
||||
|
||||
(when (eq delete-window-set-selected 'pos)
|
||||
;; Remember edges and position of point of the selected window
|
||||
;; of WINDOW'S frame.
|
||||
(setq frame-selected-window-edges
|
||||
(window-edges frame-selected-window nil nil t))
|
||||
(setq frame-selected-window-pos
|
||||
(nth 2 (posn-at-point nil frame-selected-window))))
|
||||
|
||||
;; Actually delete WINDOW.
|
||||
(delete-window-internal window)
|
||||
(window--pixel-to-total frame horizontal)
|
||||
(when (and frame-selected
|
||||
(window-parameter
|
||||
(frame-selected-window frame) 'no-other-window))
|
||||
;; `delete-window-internal' has selected a window that should
|
||||
;; not be selected, fix this here.
|
||||
(other-window -1 frame))
|
||||
|
||||
;; If we deleted the selected window of WINDOW's frame, choose
|
||||
;; another one based on `delete-window-set-selected'. Note
|
||||
;; that both `window-at-pos' and `get-mru-window' may fail to
|
||||
;; produce a suitable window in which case we will fall back on
|
||||
;; its frame's first window, chosen by `delete-window-internal'.
|
||||
(cond
|
||||
((window-live-p frame-selected-window))
|
||||
((and frame-selected-window-pos
|
||||
;; We have a recorded position of point of the previously
|
||||
;; selected window. Try to find the window that is now
|
||||
;; at that position.
|
||||
(let ((new-frame-selected-window
|
||||
(window-at-pos
|
||||
(+ (nth 0 frame-selected-window-edges)
|
||||
(car frame-selected-window-pos))
|
||||
(+ (nth 1 frame-selected-window-edges)
|
||||
(cdr frame-selected-window-pos))
|
||||
frame t)))
|
||||
(and new-frame-selected-window
|
||||
;; Select window at WINDOW's position at point.
|
||||
(set-frame-selected-window
|
||||
frame new-frame-selected-window)))))
|
||||
((and (eq delete-window-set-selected 'mru)
|
||||
;; Try to use the most recently used window.
|
||||
(let ((mru-window (get-mru-window frame nil nil t)))
|
||||
(and mru-window
|
||||
(set-frame-selected-window frame mru-window)))))
|
||||
((and (window-parameter
|
||||
(frame-selected-window frame) 'no-other-window)
|
||||
;; If `delete-window-internal' selected a window with a
|
||||
;; non-nil 'no-other-window' parameter as its frame's
|
||||
;; selected window, try to choose another one.
|
||||
(catch 'found
|
||||
(walk-window-tree
|
||||
(lambda (other)
|
||||
(unless (window-parameter other 'no-other-window)
|
||||
(set-frame-selected-window frame other)
|
||||
(throw 'found t)))
|
||||
frame))))
|
||||
(t
|
||||
;; Record the window chosen by `delete-window-internal'.
|
||||
(set-frame-selected-window
|
||||
frame (frame-selected-window frame))))
|
||||
|
||||
(window--check frame)
|
||||
;; Always return nil.
|
||||
nil))))
|
||||
|
25
src/window.c
25
src/window.c
@ -5148,15 +5148,13 @@ Signal an error when WINDOW is the only window on its frame. */)
|
||||
adjust_frame_glyphs (f);
|
||||
|
||||
if (!WINDOW_LIVE_P (FRAME_SELECTED_WINDOW (f)))
|
||||
/* We deleted the frame's selected window. */
|
||||
/* We apparently deleted the frame's selected window; use the
|
||||
frame's first window as substitute but don't record it yet.
|
||||
`delete-window' may have something better up its sleeves. */
|
||||
{
|
||||
/* Use the frame's first window as fallback ... */
|
||||
Lisp_Object new_selected_window = Fframe_first_window (frame);
|
||||
/* ... but preferably use its most recently used window. */
|
||||
Lisp_Object mru_window;
|
||||
|
||||
/* `get-mru-window' might fail for some reason so play it safe
|
||||
- promote the first window _without recording it_ first. */
|
||||
if (EQ (FRAME_SELECTED_WINDOW (f), selected_window))
|
||||
Fselect_window (new_selected_window, Qt);
|
||||
else
|
||||
@ -5164,24 +5162,9 @@ Signal an error when WINDOW is the only window on its frame. */)
|
||||
last selected window on F was an active minibuffer, we
|
||||
want to return to it on a later Fselect_frame. */
|
||||
fset_selected_window (f, new_selected_window);
|
||||
|
||||
unblock_input ();
|
||||
|
||||
/* Now look whether `get-mru-window' gets us something. */
|
||||
mru_window = call1 (Qget_mru_window, frame);
|
||||
if (WINDOW_LIVE_P (mru_window)
|
||||
&& EQ (XWINDOW (mru_window)->frame, frame))
|
||||
new_selected_window = mru_window;
|
||||
|
||||
/* If all ended up well, we now promote the mru window. */
|
||||
if (EQ (FRAME_SELECTED_WINDOW (f), selected_window))
|
||||
Fselect_window (new_selected_window, Qnil);
|
||||
else
|
||||
fset_selected_window (f, new_selected_window);
|
||||
}
|
||||
else
|
||||
unblock_input ();
|
||||
|
||||
unblock_input ();
|
||||
FRAME_WINDOW_CHANGE (f) = true;
|
||||
}
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user