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
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}.
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
(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:
(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)))
(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:
(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:
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)
;; 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.
`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))
(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))
(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))))