Commit 8783becb authored by Martin Rudalics's avatar Martin Rudalics

New buffer display action function 'display-buffer-in-direction'

* lisp/window.el (windows-sharing-edge)
(window--try-to-split-window-in-direction)
(display-buffer-in-direction): New functions.
* doc/lispref/windows.texi (Buffer Display Action Functions):
Describe new action function 'display-buffer-in-direction'.
(Buffer Display Action Alists): Describe new entry 'direction'.
Amend description of 'window' entry.
* etc/NEWS: Mention 'display-buffer-in-direction' and 'direction'
and 'window' action alist entries.
parent b87e5eea
Pipeline #1729 failed with stage
in 50 minutes and 48 seconds
......@@ -2601,6 +2601,63 @@ window and displaying the buffer in that window. It can fail if all
windows are dedicated to other buffers (@pxref{Dedicated Windows}).
@end defun
@defun display-buffer-in-direction buffer alist
This function tries to display @var{buffer} at a location specified by
@var{alist}. For this purpose, @var{alist} should contain a
@code{direction} entry whose value is one of @code{left}, @code{above}
(or @code{up}), @code{right} and @code{below} (or @code{down}). Other
values are usually interpreted as @code{below}.
If @var{alist} also contains a @code{window} entry, its value
specifies a reference window. That value can be a special symbol like
@code{main} which stands for the selected frame's main window
(@pxref{Side Window Options and Functions}) or @code{root} standing
for the selected frame's root window (@pxref{Windows and Frames}). It
can also specify an arbitrary valid window. Any other value (or
omitting the @code{window} entry entirely) means to use the selected
window as reference window.
This function first tries to reuse a window in the specified direction
that already shows @var{buffer}. If no such window exists, it tries
to split the reference window in order to produce a new window in the
specified direction. If this fails as well, it will try to display
@var{buffer} in an existing window in the specified direction. In
either case, the window chosen will appear on the side of the
reference window specified by the @code{direction} entry, sharing at
least one edge with the reference window.
If the reference window is live, the edge the chosen window will share
with it is always the opposite of the one specified by the
@code{direction} entry. For example, if the value of the
@code{direction} entry is @code{left}, the chosen window's right edge
coordinate (@pxref{Coordinates and Windows}) will equal the reference
window's left edge coordinate.
If the reference window is internal, a reused window must share with
it the edge specified by the @code{direction} entry. Hence if, for
example, the reference window is the frame's root window and the value
of the @code{direction} entry is @code{left}, a reused window must be
on the left of the frame. This means that the left edge coordinate of
the chosen window and that of the reference window are the same.
A new window, however, will be created by splitting the reference
window such that the chosen window will share the opposite edge with
the reference window. In our example, a new root window would be
created with a new live window and the reference window as its
children. The chosen window's right edge coordinate would then equal
the left edge coordinate of the reference window. Its left edge
coordinate would equal the left edge coordinate of the frame's new
root window.
Four special values for @code{direction} entries allow to implicitly
specify the selected frame's main window as the reference window:
@code{leftmost}, @code{top}, @code{rightmost} and @code{bottom}. This
means that instead of, for example, @w{@code{(direction . left)
(window . main)}} one can just specify @w{@code{(direction
. leftmost)}}. An existing @code{window} @var{alist} entry is ignored
in such cases.
@end defun
@defun display-buffer-below-selected buffer alist
This function tries to display @var{buffer} in a window below the
selected window. If there is a window below the selected one and that
......@@ -2934,12 +2991,20 @@ If non-@code{nil}, the value specifies the slot of the side window
supposed to display the buffer. This entry is used only by
@code{display-buffer-in-side-window}.
@vindex direction@r{, a buffer display action alist entry}
@item direction
The value specifies a direction which, together with a @code{window}
entry, allows @code{display-buffer-in-direction} to determine the
location of the window to display the buffer.
@vindex window@r{, a buffer display action alist entry}
@item window
The value specifies a window that is in some way related to the window
chosen by @code{display-buffer}. This entry is currently used by
@code{display-buffer-in-atom-window} to indicate the window on whose
side the new window shall be created.
side the new window shall be created. It is also used by
@code{display-buffer-in-direction} to specify the reference window on
whose side the resulting window shall appear.
@vindex allow-no-window@r{, a buffer display action alist entry}
@item allow-no-window
......
......@@ -1778,6 +1778,11 @@ detailed explanation of the new behavior.
This option allows to automatically resize minibuffer-only frames
similarly to how minibuffer windows are resized on "normal" frames.
+++
** New buffer display action function 'display-buffer-in-direction'.
This function allows to specify the location of the window chosen by
'display-buffer' in various ways.
+++
** New buffer display action alist entry 'dedicated'.
Such an entry allows to specify the dedicated status of a window
......@@ -1789,6 +1794,16 @@ Such an entry allows to specify a minimum height of the window used
for displaying a buffer. 'display-buffer-below-selected' is the only
action function to respect it at the moment.
+++
** New buffer display action alist entry 'direction'.
This entry is used to specify the location of the window chosen by
'display-buffer-in-direction'.
+++
** Additional meaning of display action alist entry 'window'.
A 'window' entry can now also specify a reference window for
'display-buffer-in-direction'.
+++
** The function 'assoc-delete-all' now takes an optional predicate argument.
......
......@@ -7534,6 +7534,152 @@ be added to ALIST."
(unless (cdr (assq 'inhibit-switch-frame alist))
(window--maybe-raise-frame frame)))))
(defun windows-sharing-edge (&optional window edge within)
"Return list of live windows sharing the same edge with WINDOW.
WINDOW must be a valid window and defaults to the selected one.
EDGE stands for the edge to share and must be either 'left',
'above', 'right' or 'below'. Omitted or nil, EDGE defaults to
'left'.
WITHIN nil means to find a live window that shares the opposite
EDGE with WINDOW. For example, if EDGE equals 'left', WINDOW has
to share (part of) the right edge of any window returned. WITHIN
non-nil means to find all live windows that share the same EDGE
with WINDOW (Window must be internal in this case). So if EDGE
equals 'left', WINDOW's left edge has to fully encompass the left
edge of any window returned."
(setq window (window-normalize-window window))
(setq edge (or edge 'left))
(when (and within (window-live-p window))
(error "Cannot share edge from within live window %s" window))
(let ((window-edges (window-edges window nil nil t))
(horizontal (memq edge '(left right)))
(n (pcase edge
('left 0) ('above 1) ('right 2) ('below 3))))
(unless (numberp n)
(error "Invalid EDGE %s" edge))
(let ((o (mod (+ 2 n) 4))
(p (if horizontal 1 0))
(q (if horizontal 3 2))
windows)
(walk-window-tree
(lambda (other)
(let ((other-edges (window-edges other nil nil t)))
(when (and (not (eq window other))
(= (nth n window-edges)
(nth (if within n o) other-edges))
(cond
((= (nth p window-edges) (nth p other-edges)))
((< (nth p window-edges) (nth p other-edges))
(< (nth p other-edges) (nth q window-edges)))
(t
(< (nth p window-edges) (nth q other-edges)))))
(setq windows (cons other windows)))))
(window-frame window) nil 'nomini)
(reverse windows))))
(defun window--try-to-split-window-in-direction (window direction alist)
"Try to split WINDOW in DIRECTION.
DIRECTION is passed as SIDE argument to `split-window-no-error'.
ALIST is a buffer display alist."
(and (not (frame-parameter (window-frame window) 'unsplittable))
(let* ((window-combination-limit
;; When `window-combination-limit' equals
;; `display-buffer' or equals `resize-window' and a
;; `window-height' or `window-width' alist entry are
;; present, bind it to t so resizing steals space
;; preferably from the window that was split.
(if (or (eq window-combination-limit 'display-buffer)
(and (eq window-combination-limit 'window-size)
(or (cdr (assq 'window-height alist))
(cdr (assq 'window-width alist)))))
t
window-combination-limit))
(new-window (split-window-no-error window nil direction)))
(and (window-live-p new-window) new-window))))
(defun display-buffer-in-direction (buffer alist)
"Try to display BUFFER in a direction specified by ALIST.
ALIST has to contain a 'direction' entry whose value should be
one of 'left', 'above' (or 'up'), 'right', and 'below' (or
'down'). Other values are usually interpreted as 'below'.
If ALIST also contains a 'window' entry, its value specifies a
reference window. That value can be a special symbol like
'main' (which stands for the selected frame's main window) or
'root' (standings for the selected frame's root window) or an
arbitrary valid window. Any other value (or omitting the
'window' entry) means to use the selected window as reference
window.
This function tries to reuse or split a window such that the
window produced this way is on the side of the reference window
specified by the 'direction' entry.
Four special values for 'direction' entries allow to implicitly
specify the selected frame's main window as reference window:
'leftmost', 'top', 'rightmost' and 'bottom'. Hence, instead of
'(direction . left) (window . main)' one can simply write
'(direction . leftmost)'."
(let ((direction (cdr (assq 'direction alist))))
(when direction
(let ((window (cdr (assq 'window alist)))
within windows other-window-shows-buffer other-window)
;; Sanitize WINDOW.
(cond
((or (eq window 'main)
(memq direction '(top bottom leftmost rightmost)))
(setq window (window-main-window)))
((eq window 'root)
(setq window (frame-root-window)))
((window-valid-p window))
(t
(setq window (selected-window))))
(setq within (not (window-live-p window)))
;; Sanitize DIRECTION
(cond
((memq direction '(left above right below)))
((eq direction 'leftmost)
(setq direction 'left))
((memq direction '(top up))
(setq direction 'above))
((eq direction 'rightmost)
(setq direction 'right))
((memq direction '(bottom down))
(setq direction 'below))
(t
(setq direction 'below)))
(setq alist
(append alist
`(,(if temp-buffer-resize-mode
'(window-height . resize-temp-buffer-window)
'(window-height . fit-window-to-buffer))
,(when temp-buffer-resize-mode
'(preserve-size . (nil . t))))))
(setq windows (windows-sharing-edge window direction within))
(dolist (other windows)
(cond
((and (not other-window-shows-buffer)
(eq buffer (window-buffer other)))
(setq other-window-shows-buffer t)
(setq other-window other))
((not other-window)
(setq other-window other))))
(or (and other-window-shows-buffer
(window--display-buffer buffer other-window 'reuse alist))
(and (setq other-window
(window--try-to-split-window-in-direction
window direction alist))
(window--display-buffer buffer other-window 'window alist))
(and (setq window other-window)
(not (window-dedicated-p other-window))
(not (window-minibuffer-p other-window))
(window--display-buffer buffer other-window 'reuse alist)))))))
;; This should be rewritten as
;; (display-buffer-in-direction buffer (cons '(direction . below) alist))
(defun display-buffer-below-selected (buffer alist)
"Try displaying BUFFER in a window below the selected window.
If there is a window below the selected one and that window
......@@ -7589,6 +7735,8 @@ must also contain a 'window-height' entry with the same value."
(display-buffer--maybe-pop-up-frame buffer alist)
(display-buffer-at-bottom buffer alist))))
;; This should be rewritten as
;; (display-buffer-in-direction buffer (cons '(direction . bottom) alist))
(defun display-buffer-at-bottom (buffer alist)
"Try displaying BUFFER in a window at the bottom of the selected frame.
This either reuses such a window provided it shows BUFFER
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment