2018-06-15 02:58:12 +02:00
|
|
|
;;; ui/popup/autoload/popup.el -*- lexical-binding: t; -*-
|
2018-05-25 00:46:11 +02:00
|
|
|
|
2018-01-06 01:23:22 -05:00
|
|
|
(defun +popup--remember (windows)
|
|
|
|
"Remember WINDOWS (a list of windows) for later restoration."
|
|
|
|
(cl-assert (cl-every #'windowp windows) t)
|
|
|
|
(setq +popup--last
|
|
|
|
(cl-loop for w in windows
|
2018-01-06 13:30:59 -05:00
|
|
|
collect (cons (window-buffer w)
|
2018-01-06 01:23:22 -05:00
|
|
|
(window-state-get w)))))
|
|
|
|
|
2018-01-06 04:52:37 -05:00
|
|
|
(defun +popup--kill-buffer (buffer ttl)
|
2018-01-06 01:23:22 -05:00
|
|
|
"Tries to kill BUFFER, as was requested by a transient timer. If it fails, eg.
|
|
|
|
the buffer is visible, then set another timer and try again later."
|
|
|
|
(when (buffer-live-p buffer)
|
2018-03-19 04:45:04 -04:00
|
|
|
(let ((inhibit-quit t)
|
|
|
|
(kill-buffer-hook (remq '+popup|kill-buffer-hook kill-buffer-hook)))
|
2018-06-12 14:13:12 +02:00
|
|
|
(cond ((get-buffer-window buffer)
|
2018-03-19 04:45:04 -04:00
|
|
|
(with-current-buffer buffer
|
|
|
|
(setq +popup--timer
|
|
|
|
(run-at-time ttl nil #'+popup--kill-buffer buffer ttl))))
|
2018-06-12 14:13:12 +02:00
|
|
|
((eq ttl 0)
|
|
|
|
(kill-buffer buffer))
|
2018-03-19 04:45:04 -04:00
|
|
|
((with-demoted-errors "Error killing transient buffer: %s"
|
2018-06-13 23:25:40 +02:00
|
|
|
(with-current-buffer buffer
|
|
|
|
(let (confirm-kill-processes)
|
|
|
|
(when-let* ((process (get-buffer-process buffer)))
|
|
|
|
(kill-process process))
|
|
|
|
(let (kill-buffer-hook kill-buffer-query-functions)
|
|
|
|
(kill-buffer buffer))))))))))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
2018-01-11 01:07:39 -05:00
|
|
|
(defun +popup--init (window &optional alist)
|
2018-01-06 01:23:22 -05:00
|
|
|
"Initializes a popup window. Run any time a popup is opened. It sets the
|
|
|
|
default window parameters for popup windows, clears leftover transient timers
|
|
|
|
and enables `+popup-buffer-mode'."
|
|
|
|
(with-selected-window window
|
2018-06-23 22:18:44 +02:00
|
|
|
(setq alist (delq (assq 'actions alist) alist))
|
2018-01-11 01:07:39 -05:00
|
|
|
(when (and alist +popup--populate-wparams)
|
2018-01-07 13:06:42 -05:00
|
|
|
;; Emacs 26+ will automatically map the window-parameters alist entry to
|
|
|
|
;; the popup window, so we need this for Emacs 25.x users
|
|
|
|
(dolist (param (cdr (assq 'window-parameters alist)))
|
|
|
|
(set-window-parameter window (car param) (cdr param))))
|
|
|
|
(set-window-parameter window 'popup t)
|
2018-01-11 01:07:39 -05:00
|
|
|
(set-window-parameter window 'delete-window #'+popup--delete-window)
|
|
|
|
(set-window-parameter window 'delete-other-windows #'+popup/close-all)
|
2018-01-09 16:40:44 -05:00
|
|
|
(set-window-dedicated-p window 'popup)
|
2018-01-15 00:39:23 -05:00
|
|
|
(window-preserve-size
|
|
|
|
window (memq (window-parameter window 'window-side)
|
|
|
|
'(left right))
|
|
|
|
t)
|
2018-01-07 13:06:42 -05:00
|
|
|
(+popup-buffer-mode +1)
|
|
|
|
(run-hooks '+popup-create-window-hook)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
2018-01-11 01:07:39 -05:00
|
|
|
(defun +popup--delete-window (window)
|
2018-01-06 01:23:22 -05:00
|
|
|
"Do housekeeping before destroying a popup window.
|
|
|
|
|
|
|
|
+ Disables `+popup-buffer-mode' so that any hooks attached to it get a chance to
|
|
|
|
run and do cleanup of its own.
|
|
|
|
+ Either kills the buffer or sets a transient timer, if the window has a
|
|
|
|
`transient' window parameter (see `+popup-window-parameters').
|
|
|
|
+ And finally deletes the window!"
|
2018-01-07 02:33:57 -05:00
|
|
|
(let ((buffer (window-buffer window))
|
2018-06-23 22:18:44 +02:00
|
|
|
(inhibit-quit t))
|
|
|
|
(and (buffer-file-name buffer)
|
|
|
|
(buffer-modified-p buffer)
|
|
|
|
(let ((autosave (+popup-parameter 'autosave window)))
|
|
|
|
(cond ((eq autosave 't))
|
|
|
|
((null autosave)
|
|
|
|
(y-or-n-p "Popup buffer is modified. Save it?"))
|
|
|
|
((functionp autosave)
|
|
|
|
(funcall autosave buffer))))
|
|
|
|
(with-current-buffer buffer (save-buffer)))
|
2018-01-06 01:23:22 -05:00
|
|
|
(let ((ignore-window-parameters t))
|
2018-06-25 19:28:09 +02:00
|
|
|
(if-let* ((wconf (window-parameter window 'saved-wconf)))
|
|
|
|
(set-window-configuration wconf)
|
|
|
|
(delete-window window)))
|
2018-01-06 01:23:22 -05:00
|
|
|
(unless (window-live-p window)
|
|
|
|
(with-current-buffer buffer
|
2018-06-23 22:18:44 +02:00
|
|
|
(set-buffer-modified-p nil)
|
2018-01-06 01:23:22 -05:00
|
|
|
(+popup-buffer-mode -1)
|
2018-01-07 02:33:57 -05:00
|
|
|
(unless +popup--inhibit-transient
|
2018-06-23 22:18:44 +02:00
|
|
|
(let ((ttl (+popup-parameter 'ttl window)))
|
|
|
|
(when (eq ttl 't)
|
|
|
|
(setq ttl (plist-get +popup-defaults :ttl)))
|
|
|
|
(cond ((null ttl))
|
|
|
|
((functionp ttl)
|
|
|
|
(funcall ttl buffer))
|
|
|
|
((not (integerp ttl))
|
|
|
|
(signal 'wrong-type-argument (list 'integerp ttl)))
|
|
|
|
((= ttl 0)
|
|
|
|
(+popup--kill-buffer buffer 0))
|
2018-07-01 00:57:27 +02:00
|
|
|
((add-hook 'kill-buffer-hook #'+popup|kill-buffer-hook nil t)
|
|
|
|
(setq +popup--timer
|
2018-06-23 22:18:44 +02:00
|
|
|
(run-at-time ttl nil #'+popup--kill-buffer
|
|
|
|
buffer ttl))))))))))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
(defun +popup--normalize-alist (alist)
|
|
|
|
"Merge `+popup-default-alist' and `+popup-default-parameters' with ALIST."
|
2018-06-23 22:18:44 +02:00
|
|
|
(when alist
|
|
|
|
(let ((alist ; handle defaults
|
|
|
|
(cl-remove-duplicates
|
|
|
|
(append alist +popup-default-alist)
|
|
|
|
:key #'car :from-end t))
|
|
|
|
(parameters
|
|
|
|
(cl-remove-duplicates
|
|
|
|
(append (cdr (assq 'window-parameters alist))
|
|
|
|
+popup-default-parameters)
|
|
|
|
:key #'car :from-end t)))
|
|
|
|
;; handle `size'
|
|
|
|
(when-let* ((size (cdr (assq 'size alist)))
|
|
|
|
(side (or (cdr (assq 'side alist)) 'bottom))
|
|
|
|
(param (if (memq side '(left right))
|
|
|
|
'window-width
|
|
|
|
'window-height)))
|
|
|
|
(setq list (assq-delete-all 'size alist))
|
|
|
|
(setcdr (assq param alist) size))
|
|
|
|
(setcdr (assq 'window-parameters alist)
|
|
|
|
parameters)
|
|
|
|
alist)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Public library
|
|
|
|
;;
|
|
|
|
|
|
|
|
;;;###autoload
|
2018-01-07 21:52:54 -05:00
|
|
|
(defun +popup-buffer-p (&optional buffer)
|
2018-04-04 05:27:33 -04:00
|
|
|
"Return non-nil if BUFFER is a popup buffer. Defaults to the current buffer."
|
2018-06-17 01:59:51 +02:00
|
|
|
(when +popup-mode
|
2018-06-23 22:18:44 +02:00
|
|
|
(let ((buffer (or buffer (current-buffer))))
|
|
|
|
(and (bufferp buffer)
|
|
|
|
(buffer-live-p buffer)
|
|
|
|
(buffer-local-value '+popup-buffer-mode buffer)
|
|
|
|
buffer))))
|
2018-01-07 21:52:54 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-window-p (&optional window)
|
2018-04-04 05:27:33 -04:00
|
|
|
"Return non-nil if WINDOW is a popup window. Defaults to the current window."
|
2018-06-17 01:59:51 +02:00
|
|
|
(when +popup-mode
|
2018-06-23 22:18:44 +02:00
|
|
|
(let ((window (or window (selected-window))))
|
|
|
|
(and (windowp window)
|
|
|
|
(window-live-p window)
|
|
|
|
(window-parameter window 'popup)
|
|
|
|
window))))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-buffer (buffer &optional alist)
|
|
|
|
"Open BUFFER in a popup window. ALIST describes its features."
|
2018-06-18 02:26:05 +02:00
|
|
|
(let* ((origin (selected-window))
|
|
|
|
(window-min-height 3)
|
|
|
|
(alist (+popup--normalize-alist alist))
|
|
|
|
(actions (or (cdr (assq 'actions alist))
|
|
|
|
+popup-default-display-buffer-actions)))
|
|
|
|
(when-let* ((popup (cl-loop for func in actions
|
|
|
|
if (funcall func buffer alist)
|
|
|
|
return it)))
|
|
|
|
(+popup--init popup alist)
|
2018-04-23 01:21:29 -04:00
|
|
|
(unless +popup--inhibit-select
|
2018-06-18 02:26:05 +02:00
|
|
|
(let ((select (+popup-parameter 'select popup)))
|
2018-04-23 01:21:29 -04:00
|
|
|
(if (functionp select)
|
2018-06-18 02:26:05 +02:00
|
|
|
(funcall select popup origin)
|
|
|
|
(select-window (if select popup origin)))))
|
|
|
|
popup)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-parameter (parameter &optional window)
|
2018-01-07 05:42:33 -05:00
|
|
|
"Fetch the window PARAMETER (symbol) of WINDOW"
|
2018-01-06 01:23:22 -05:00
|
|
|
(window-parameter (or window (selected-window)) parameter))
|
|
|
|
|
2018-01-07 02:33:57 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-parameter-fn (parameter &optional window &rest args)
|
|
|
|
"Fetch the window PARAMETER (symbol) of WINDOW. If it is a function, run it
|
|
|
|
with ARGS to get its return value."
|
|
|
|
(let ((val (+popup-parameter parameter window)))
|
|
|
|
(if (functionp val)
|
|
|
|
(apply val args)
|
|
|
|
val)))
|
|
|
|
|
2018-01-06 01:23:22 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-windows ()
|
|
|
|
"Returns a list of all popup windows."
|
2018-01-07 21:52:54 -05:00
|
|
|
(cl-remove-if-not #'+popup-window-p (window-list)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
2018-01-07 18:22:56 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-shrink-to-fit (&optional window)
|
|
|
|
"Shrinks WINDOW to fit the buffer contents, if the buffer isn't empty.
|
|
|
|
|
|
|
|
Uses `shrink-window-if-larger-than-buffer'."
|
|
|
|
(unless window
|
|
|
|
(setq window (selected-window)))
|
|
|
|
(unless (= (- (point-max) (point-min)) 0)
|
|
|
|
(shrink-window-if-larger-than-buffer window)))
|
|
|
|
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;
|
|
|
|
;; Hooks
|
|
|
|
;;
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|adjust-fringes ()
|
|
|
|
"Hides the fringe in popup windows, restoring them if `+popup-buffer-mode' is
|
|
|
|
disabled."
|
2018-06-17 01:59:51 +02:00
|
|
|
(let ((f (if (bound-and-true-p +popup-buffer-mode) 0)))
|
2018-01-06 01:23:22 -05:00
|
|
|
(set-window-fringes nil f f fringes-outside-margins)))
|
|
|
|
|
2018-06-17 02:01:11 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|adjust-margins ()
|
|
|
|
"Creates padding for the popup window determined by `+popup-margin-width',
|
|
|
|
restoring it if `+popup-buffer-mode' is disabled."
|
|
|
|
(when +popup-margin-width
|
2018-06-29 02:35:33 +02:00
|
|
|
(unless (memq (window-parameter nil 'window-side) '(left right))
|
|
|
|
(let ((m (if (bound-and-true-p +popup-buffer-mode) +popup-margin-width)))
|
|
|
|
(set-window-margins nil m m)))))
|
2018-06-17 02:01:11 +02:00
|
|
|
|
2018-01-06 01:23:22 -05:00
|
|
|
;;;###autoload
|
2018-03-18 02:37:10 -04:00
|
|
|
(defun +popup|set-modeline-on-enable ()
|
2018-01-06 01:23:22 -05:00
|
|
|
"Don't show modeline in popup windows without a `modeline' window-parameter.
|
|
|
|
|
|
|
|
+ If one exists and it's a symbol, use `doom-modeline' to grab the format.
|
|
|
|
+ If non-nil, show the mode-line as normal.
|
2018-01-07 02:33:57 -05:00
|
|
|
+ If nil (or omitted), then hide the modeline entirely (the default).
|
|
|
|
+ If a function, it takes the current buffer as its argument and must return one
|
|
|
|
of the above values."
|
2018-06-17 01:59:51 +02:00
|
|
|
(when (bound-and-true-p +popup-buffer-mode)
|
2018-06-23 22:18:44 +02:00
|
|
|
(let ((modeline (+popup-parameter 'modeline)))
|
2018-03-18 02:37:10 -04:00
|
|
|
(cond ((eq modeline 't))
|
|
|
|
((or (eq modeline 'nil)
|
|
|
|
(null modeline))
|
2018-06-23 22:18:44 +02:00
|
|
|
;; TODO use `mode-line-format' window parameter instead (emacs 26+)
|
2018-03-18 02:37:10 -04:00
|
|
|
(hide-mode-line-mode +1))
|
2018-06-23 22:18:44 +02:00
|
|
|
((functionp modeline)
|
|
|
|
(funcall modeline))
|
2018-03-18 02:37:10 -04:00
|
|
|
((symbolp modeline)
|
|
|
|
(when-let* ((hide-mode-line-format (doom-modeline modeline)))
|
|
|
|
(hide-mode-line-mode +1)))))))
|
2018-06-18 02:26:05 +02:00
|
|
|
(put '+popup|set-modeline-on-enable 'permanent-local-hook t)
|
2018-03-18 02:37:10 -04:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|unset-modeline-on-disable ()
|
|
|
|
"Restore the modeline when `+popup-buffer-mode' is deactivated."
|
2018-06-17 01:59:51 +02:00
|
|
|
(when (and (not (bound-and-true-p +popup-buffer-mode))
|
2018-03-18 02:37:10 -04:00
|
|
|
(bound-and-true-p hide-mode-line-mode))
|
|
|
|
(hide-mode-line-mode -1)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|close-on-escape ()
|
|
|
|
"If called inside a popup, try to close that popup window (see
|
|
|
|
`+popup/close'). If called outside, try to close all popup windows (see
|
|
|
|
`+popup/close-all')."
|
2018-01-07 21:52:54 -05:00
|
|
|
(if (+popup-window-p)
|
2018-01-07 18:39:42 -05:00
|
|
|
(+popup/close)
|
|
|
|
(+popup/close-all)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
2018-01-07 05:44:58 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|cleanup-rules ()
|
|
|
|
"Cleans up any duplicate popup rules."
|
|
|
|
(interactive)
|
2018-06-23 22:18:44 +02:00
|
|
|
(setq +popup--display-buffer-alist
|
|
|
|
(cl-delete-duplicates +popup--display-buffer-alist
|
|
|
|
:key #'car :test #'equal :from-end t))
|
2018-01-07 05:44:58 -05:00
|
|
|
(when +popup-mode
|
|
|
|
(setq display-buffer-alist +popup--display-buffer-alist)))
|
|
|
|
|
2018-01-07 22:13:42 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup|kill-buffer-hook ()
|
|
|
|
"TODO"
|
2018-01-11 01:07:39 -05:00
|
|
|
(when-let* ((window (get-buffer-window)))
|
|
|
|
(when (+popup-window-p window)
|
|
|
|
(let ((+popup--inhibit-transient t))
|
|
|
|
(+popup--delete-window window)))))
|
2018-01-07 22:13:42 -05:00
|
|
|
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;
|
|
|
|
;; Commands
|
|
|
|
;;
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defalias 'other-popup #'+popup/other)
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/other ()
|
|
|
|
"Cycle through popup windows, like `other-window'. Ignores regular windows."
|
|
|
|
(interactive)
|
|
|
|
(let ((popups (+popup-windows))
|
|
|
|
(window (selected-window)))
|
|
|
|
(unless popups
|
|
|
|
(user-error "No popups are open"))
|
2018-01-07 21:52:54 -05:00
|
|
|
(select-window (if (+popup-window-p)
|
2018-01-06 01:23:22 -05:00
|
|
|
(or (car-safe (cdr (memq window popups)))
|
|
|
|
(car (delq window popups))
|
|
|
|
(car popups))
|
|
|
|
(car popups)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/close (&optional window force-p)
|
|
|
|
"Close WINDOW, if it's a popup window.
|
|
|
|
|
2018-01-06 03:03:02 -05:00
|
|
|
This will do nothing if the popup's `quit' window parameter is either nil or
|
|
|
|
'other. This window parameter is ignored if FORCE-P is non-nil."
|
2018-01-06 01:23:22 -05:00
|
|
|
(interactive
|
|
|
|
(list (selected-window)
|
|
|
|
current-prefix-arg))
|
2018-06-23 22:18:44 +02:00
|
|
|
(let ((window (or window (selected-window))))
|
|
|
|
(when (and (+popup-window-p window)
|
|
|
|
(or force-p
|
|
|
|
(memq (+popup-parameter-fn 'quit window window)
|
|
|
|
'(t current))))
|
|
|
|
(when +popup--remember-last
|
|
|
|
(+popup--remember (list window)))
|
|
|
|
(delete-window window)
|
|
|
|
t)))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/close-all (&optional force-p)
|
|
|
|
"Close all open popup windows.
|
|
|
|
|
2018-01-06 02:42:53 -05:00
|
|
|
This will ignore popups with an `quit' parameter that is either nil or 'current.
|
|
|
|
This window parameter is ignored if FORCE-P is non-nil."
|
2018-01-06 01:23:22 -05:00
|
|
|
(interactive "P")
|
|
|
|
(let (targets +popup--remember-last)
|
|
|
|
(dolist (window (+popup-windows))
|
|
|
|
(when (or force-p
|
2018-01-07 02:33:57 -05:00
|
|
|
(memq (+popup-parameter-fn 'quit window window)
|
2018-01-06 01:23:22 -05:00
|
|
|
'(t other)))
|
|
|
|
(push window targets)))
|
|
|
|
(when targets
|
|
|
|
(+popup--remember targets)
|
|
|
|
(mapc #'delete-window targets)
|
|
|
|
t)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/toggle ()
|
|
|
|
"If popups are open, close them. If they aren't, restore the last one or open
|
|
|
|
the message buffer in a popup window."
|
|
|
|
(interactive)
|
2018-01-07 18:39:42 -05:00
|
|
|
(let ((+popup--inhibit-transient t))
|
2018-01-11 01:07:39 -05:00
|
|
|
(cond ((+popup-windows) (+popup/close-all t))
|
2018-01-07 18:39:42 -05:00
|
|
|
((ignore-errors (+popup/restore)))
|
|
|
|
((display-buffer (get-buffer "*Messages*"))))))
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/restore ()
|
|
|
|
"Restore the last popups that were closed, if any."
|
|
|
|
(interactive)
|
|
|
|
(unless +popup--last
|
|
|
|
(error "No popups to restore"))
|
2018-01-06 04:56:12 -05:00
|
|
|
(cl-loop for (buffer . state) in +popup--last
|
2018-01-06 01:23:22 -05:00
|
|
|
if (and (buffer-live-p buffer)
|
2018-01-06 13:30:59 -05:00
|
|
|
(display-buffer buffer))
|
2018-01-06 01:23:22 -05:00
|
|
|
do (window-state-put state it))
|
2018-01-09 16:42:08 -05:00
|
|
|
(setq +popup--last nil)
|
|
|
|
t)
|
2018-01-06 01:23:22 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup/raise ()
|
|
|
|
"Raise the current popup window into a regular window."
|
|
|
|
(interactive)
|
2018-01-07 21:52:54 -05:00
|
|
|
(unless (+popup-window-p)
|
2018-01-06 01:23:22 -05:00
|
|
|
(user-error "Cannot raise a non-popup window"))
|
|
|
|
(let ((window (selected-window))
|
|
|
|
(buffer (current-buffer))
|
|
|
|
+popup--remember-last)
|
2018-06-18 02:26:05 +02:00
|
|
|
(set-window-parameter window 'ttl nil)
|
2018-01-06 01:23:22 -05:00
|
|
|
(+popup/close window 'force)
|
|
|
|
(display-buffer-pop-up-window buffer nil)))
|
|
|
|
|
|
|
|
|
2018-01-06 04:56:39 -05:00
|
|
|
;;
|
|
|
|
;; Advice
|
|
|
|
;;
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup*close (&rest _)
|
|
|
|
"TODO"
|
2018-01-28 22:21:37 -05:00
|
|
|
(+popup/close nil t))
|
2018-01-06 04:56:39 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +popup*save (orig-fn &rest args)
|
|
|
|
"Sets aside all popups before executing the original function, usually to
|
|
|
|
prevent the popup(s) from messing up the UI (or vice versa)."
|
|
|
|
(save-popups! (apply orig-fn args)))
|
2018-01-09 16:43:07 -05:00
|
|
|
|
2018-06-25 19:28:09 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun +popup-display-buffer-fullframe (buffer alist)
|
|
|
|
"Displays the buffer fullscreen."
|
|
|
|
(let ((wconf (current-window-configuration)))
|
|
|
|
(when-let (window (or (display-buffer-reuse-window buffer alist)
|
|
|
|
(display-buffer-same-window buffer alist)
|
|
|
|
(display-buffer-pop-up-window buffer alist)
|
|
|
|
(display-buffer-use-some-window buffer alist)))
|
|
|
|
(set-window-parameter window 'saved-wconf wconf)
|
|
|
|
(add-to-list 'window-persistent-parameters '(saved-wconf . t))
|
|
|
|
(delete-other-windows window)
|
|
|
|
window)))
|
|
|
|
|
2018-01-09 16:43:07 -05:00
|
|
|
;;;###autoload
|
2018-06-17 02:03:05 +02:00
|
|
|
(defun +popup-display-buffer-stacked-side-window (buffer alist)
|
2018-01-11 01:05:20 -05:00
|
|
|
"A `display-buffer' action that serves as an alternative to
|
2018-06-17 02:03:05 +02:00
|
|
|
`display-buffer-in-side-window', but allows for stacking popups with the `vslot'
|
|
|
|
alist entry.
|
2018-01-11 01:05:20 -05:00
|
|
|
|
|
|
|
Accepts the same arguments as `display-buffer-in-side-window'. You must set
|
|
|
|
`window--sides-inhibit-check' to non-nil for this work properly."
|
|
|
|
(let* ((side (or (cdr (assq 'side alist)) 'bottom))
|
|
|
|
(slot (or (cdr (assq 'slot alist)) 0))
|
|
|
|
(vslot (or (cdr (assq 'vslot alist)) 0))
|
|
|
|
(left-or-right (memq side '(left right)))
|
|
|
|
(dedicated (or display-buffer-mark-dedicated 'popup)))
|
|
|
|
|
|
|
|
(cond ((not (memq side '(top bottom left right)))
|
|
|
|
(error "Invalid side %s specified" side))
|
|
|
|
((not (numberp slot))
|
|
|
|
(error "Invalid slot %s specified" slot))
|
|
|
|
((not (numberp vslot))
|
2018-02-07 01:09:32 -05:00
|
|
|
(error "Invalid vslot %s specified" vslot)))
|
2018-01-11 01:05:20 -05:00
|
|
|
|
|
|
|
(let* ((major (get-window-with-predicate
|
|
|
|
(lambda (window)
|
|
|
|
(and (eq (window-parameter window 'window-side) side)
|
|
|
|
(eq (window-parameter window 'window-vslot) vslot)))
|
|
|
|
nil t))
|
|
|
|
(reversed (window--sides-reverse-on-frame-p (selected-frame)))
|
|
|
|
(windows
|
|
|
|
(cond ((window-live-p major)
|
|
|
|
(list major))
|
|
|
|
((window-valid-p major)
|
|
|
|
(let* ((first (window-child major))
|
|
|
|
(next (window-next-sibling first))
|
|
|
|
(windows (list next first)))
|
|
|
|
(setq reversed (> (window-parameter first 'window-slot)
|
|
|
|
(window-parameter next 'window-slot)))
|
|
|
|
(while (setq next (window-next-sibling next))
|
|
|
|
(setq windows (cons next windows)))
|
|
|
|
(if reversed windows (nreverse windows))))))
|
|
|
|
(slots (if major (max 1 (window-child-count major))))
|
|
|
|
(max-slots
|
|
|
|
(nth (plist-get '(left 0 top 1 right 2 bottom 3) side)
|
|
|
|
window-sides-slots))
|
|
|
|
(window--sides-inhibit-check t)
|
|
|
|
window this-window this-slot prev-window next-window
|
|
|
|
best-window best-slot abs-slot)
|
|
|
|
|
|
|
|
(cond ((and (numberp max-slots) (<= max-slots 0))
|
|
|
|
nil)
|
|
|
|
((not windows)
|
|
|
|
(cl-letf (((symbol-function 'window--make-major-side-window-next-to)
|
|
|
|
(lambda (_side) (frame-root-window (selected-frame)))))
|
|
|
|
(when-let* ((window (window--make-major-side-window buffer side slot alist)))
|
|
|
|
(set-window-parameter window 'window-vslot vslot)
|
2018-06-23 22:18:44 +02:00
|
|
|
(add-to-list 'window-persistent-parameters '(window-vslot . writable))
|
2018-01-11 01:05:20 -05:00
|
|
|
window)))
|
|
|
|
(t
|
|
|
|
;; Scan windows on SIDE.
|
|
|
|
(catch 'found
|
|
|
|
(dolist (window windows)
|
|
|
|
(setq this-slot (window-parameter window 'window-slot))
|
|
|
|
(cond ((not (numberp this-slot)))
|
|
|
|
((= this-slot slot) ; A window with a matching slot found
|
|
|
|
(setq this-window window)
|
|
|
|
(throw 'found t))
|
|
|
|
(t
|
|
|
|
;; Check if this window has a better slot value wrt the
|
|
|
|
;; slot of the window we want.
|
|
|
|
(setq abs-slot
|
|
|
|
(if (or (and (> this-slot 0) (> slot 0))
|
|
|
|
(and (< this-slot 0) (< slot 0)))
|
|
|
|
(abs (- slot this-slot))
|
|
|
|
(+ (abs slot) (abs this-slot))))
|
|
|
|
(unless (and best-slot (<= best-slot abs-slot))
|
|
|
|
(setq best-window window)
|
|
|
|
(setq best-slot abs-slot))
|
|
|
|
(if reversed
|
|
|
|
(cond
|
|
|
|
((<= this-slot slot)
|
|
|
|
(setq next-window window))
|
|
|
|
((not prev-window)
|
|
|
|
(setq prev-window window)))
|
|
|
|
(cond
|
|
|
|
((<= this-slot slot)
|
|
|
|
(setq prev-window window))
|
|
|
|
((not next-window)
|
|
|
|
(setq next-window window))))))))
|
|
|
|
|
|
|
|
;; `this-window' is the first window with the same SLOT.
|
|
|
|
;; `prev-window' is the window with the largest slot < SLOT. A new
|
|
|
|
;; window will be created after it.
|
|
|
|
;; `next-window' is the window with the smallest slot > SLOT. A new
|
|
|
|
;; window will be created before it.
|
|
|
|
;; `best-window' is the window with the smallest absolute
|
|
|
|
;; difference of its slot and SLOT.
|
|
|
|
(or (and this-window
|
|
|
|
;; Reuse `this-window'.
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(setq window--sides-shown t))
|
|
|
|
(window--display-buffer
|
|
|
|
buffer this-window 'reuse alist dedicated))
|
|
|
|
(and (or (not max-slots) (< slots max-slots))
|
|
|
|
(or (and next-window
|
|
|
|
;; Make new window before `next-window'.
|
|
|
|
(let ((next-side (if left-or-right 'above 'left))
|
|
|
|
(window-combination-resize 'side))
|
2018-01-12 15:09:08 -05:00
|
|
|
(setq window
|
|
|
|
(ignore-errors (split-window next-window nil next-side)))))
|
2018-01-11 01:05:20 -05:00
|
|
|
(and prev-window
|
|
|
|
;; Make new window after `prev-window'.
|
|
|
|
(let ((prev-side (if left-or-right 'below 'right))
|
|
|
|
(window-combination-resize 'side))
|
2018-01-12 15:09:08 -05:00
|
|
|
(setq window
|
|
|
|
(ignore-errors (split-window prev-window nil prev-side))))))
|
2018-01-11 01:05:20 -05:00
|
|
|
(set-window-parameter window 'window-slot slot)
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(setq window--sides-shown t))
|
|
|
|
(window--display-buffer
|
|
|
|
buffer window 'window alist dedicated))
|
|
|
|
(and best-window
|
|
|
|
;; Reuse `best-window'.
|
|
|
|
(progn
|
|
|
|
;; Give best-window the new slot value.
|
|
|
|
(set-window-parameter best-window 'window-slot slot)
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(setq window--sides-shown t))
|
|
|
|
(window--display-buffer
|
|
|
|
buffer best-window 'reuse alist dedicated)))))))))
|
2018-06-17 02:07:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Emacs backwards compatibility
|
|
|
|
;;
|
|
|
|
|
|
|
|
(unless EMACS26+
|
|
|
|
(defvar window-sides-reversed nil)
|
|
|
|
|
|
|
|
(defun window--sides-reverse-on-frame-p (frame)
|
|
|
|
"Return non-nil when side windows should appear reversed on FRAME.
|
|
|
|
This uses some heuristics to guess the user's intentions when the
|
|
|
|
selected window of FRAME is a side window ."
|
|
|
|
(cond
|
|
|
|
;; Reverse when `window-sides-reversed' is t. Do not reverse when
|
|
|
|
;; `window-sides-reversed' is nil.
|
|
|
|
((memq window-sides-reversed '(nil t))
|
|
|
|
window-sides-reversed)
|
|
|
|
;; Reverse when FRAME's selected window shows a right-to-left buffer.
|
|
|
|
((let ((window (frame-selected-window frame)))
|
|
|
|
(when (and (not (window-parameter window 'window-side))
|
|
|
|
(or (not (window-minibuffer-p window))
|
|
|
|
(setq window (minibuffer-selected-window))))
|
|
|
|
(with-current-buffer (window-buffer window)
|
|
|
|
(eq bidi-paragraph-direction 'right-to-left)))))
|
|
|
|
;; Reverse when FRAME's `window-sides-main-selected-window' parameter
|
|
|
|
;; specifies a live window showing a right-to-left buffer.
|
|
|
|
((let ((window (frame-parameter
|
|
|
|
frame 'window-sides-main-selected-window)))
|
|
|
|
(when (window-live-p window)
|
|
|
|
(with-current-buffer (window-buffer window)
|
|
|
|
(eq bidi-paragraph-direction 'right-to-left)))))
|
|
|
|
;; Reverse when all windows in FRAME's main window show right-to-left
|
|
|
|
;; buffers.
|
|
|
|
(t
|
|
|
|
(catch 'found
|
|
|
|
(walk-window-subtree
|
|
|
|
(lambda (window)
|
|
|
|
(with-current-buffer (window-buffer window)
|
|
|
|
(when (eq bidi-paragraph-direction 'left-to-right)
|
|
|
|
(throw 'found nil))))
|
|
|
|
(window-main-window frame))
|
|
|
|
t))))
|
|
|
|
|
|
|
|
(defun window--make-major-side-window (buffer side slot &optional alist)
|
|
|
|
"Display BUFFER in a new major side window on the selected frame.
|
|
|
|
SIDE must be one of `left', `top', `right' or `bottom'. SLOT
|
|
|
|
specifies the slot to use. ALIST is an association list of
|
|
|
|
symbols and values as passed to `display-buffer-in-side-window'.
|
|
|
|
Return the new window, nil if its creation failed.
|
|
|
|
|
|
|
|
This is an auxiliary function of `display-buffer-in-side-window'
|
|
|
|
and may be called only if no window on SIDE exists yet."
|
|
|
|
(let* ((left-or-right (memq side '(left right)))
|
|
|
|
(next-to (window--make-major-side-window-next-to side))
|
|
|
|
(on-side (cond
|
|
|
|
((eq side 'top) 'above)
|
|
|
|
((eq side 'bottom) 'below)
|
|
|
|
(t side)))
|
|
|
|
(window--sides-inhibit-check t)
|
|
|
|
;; The following two bindings will tell `split-window' to take
|
|
|
|
;; the space for the new window from the selected frame's main
|
|
|
|
;; window and not make a new parent window unless needed.
|
|
|
|
(window-combination-resize 'side)
|
|
|
|
(window-combination-limit nil)
|
|
|
|
(window (ignore-errors (split-window next-to nil on-side))))
|
|
|
|
(when window
|
|
|
|
;; Initialize `window-side' parameter of new window to SIDE and
|
|
|
|
;; make that parameter persistent.
|
|
|
|
(set-window-parameter window 'window-side side)
|
2018-06-23 22:18:44 +02:00
|
|
|
(add-to-list 'window-persistent-parameters '(window-side . writable))
|
2018-06-17 02:07:14 +02:00
|
|
|
;; Install `window-slot' parameter of new window and make that
|
|
|
|
;; parameter persistent.
|
|
|
|
(set-window-parameter window 'window-slot slot)
|
2018-06-23 22:18:44 +02:00
|
|
|
(add-to-list 'window-persistent-parameters '(window-slot . writable))
|
2018-06-17 02:07:14 +02:00
|
|
|
;; Auto-adjust height/width of new window unless a size has been
|
|
|
|
;; explicitly requested.
|
|
|
|
(unless (if left-or-right
|
|
|
|
(cdr (assq 'window-width alist))
|
|
|
|
(cdr (assq 'window-height alist)))
|
|
|
|
(setq alist
|
|
|
|
(cons
|
|
|
|
(cons
|
|
|
|
(if left-or-right 'window-width 'window-height)
|
|
|
|
(/ (window-total-size (frame-root-window) left-or-right)
|
|
|
|
;; By default use a fourth of the size of the frame's
|
|
|
|
;; root window.
|
|
|
|
4))
|
|
|
|
alist)))
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(setq window--sides-shown t))
|
|
|
|
;; Install BUFFER in new window and return WINDOW.
|
|
|
|
(window--display-buffer buffer window 'window alist 'side))))
|
|
|
|
|
|
|
|
(advice-add #'window--sides-check :override #'ignore))
|