[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master b3dd0ce: Provide new option `delete-window-set-selected' (Bug#473
From: |
Martin Rudalics |
Subject: |
master b3dd0ce: Provide new option `delete-window-set-selected' (Bug#47300) |
Date: |
Thu, 10 Jun 2021 03:16:22 -0400 (EDT) |
branch: master
commit b3dd0ce75ba9314eb7a682e5fcf8b4cfbc67655b
Author: Martin Rudalics <rudalics@gmx.at>
Commit: Martin Rudalics <rudalics@gmx.at>
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.
---
doc/lispref/windows.texi | 47 ++++++++------
etc/NEWS | 11 ++++
lisp/window.el | 157 ++++++++++++++++++++++++++++++++++++++---------
src/window.c | 25 ++------
4 files changed, 171 insertions(+), 69 deletions(-)
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 44656c0..bcb492d 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
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
diff --git a/etc/NEWS b/etc/NEWS
index 3075fa0..06c6efd 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -605,6 +605,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
current window, and when called repeatedly will try not to reuse a
diff --git a/lisp/window.el b/lisp/window.el
index fd1c617..e7551f3 100644
--- a/lisp/window.el
+++ b/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))))
diff --git a/src/window.c b/src/window.c
index 2d98ae5..db324ef 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. */)
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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- master b3dd0ce: Provide new option `delete-window-set-selected' (Bug#47300),
Martin Rudalics <=