Compare commits

...

22 Commits

Author SHA1 Message Date
8c396a11f5 Don’t err in pip-frame-remove-buffer if there is no PIP frame
This is useful e.g. to prevent the error when a PIP frame with a
temporarily displayed buffer is deleted before the buffer timer
attempts to remove the buffer.
2022-08-02 21:14:04 +02:00
e8b16420f8 README: Add installation section 2022-07-26 20:07:08 +02:00
7a75e73f88 Silence checkdoc messages about docstrings of internal objects
Internal objects here don’t have and shouldn’t have documentation
strings because:

- There is nothing useful to specify in them.
- They are not a stable API to be used.
- They would pollute the source file and make it larger.
2022-07-06 10:10:46 +02:00
3e37e3ad0c Move Code: label before the beginning of code
From the place where it was inserted by checkdoc.
2022-07-05 13:23:23 +02:00
57a18de9aa Use Version instead of Package-Version in package headers 2022-07-04 19:01:55 +02:00
23721d0a55 Add some useless stuff to make checkdoc happier 2022-07-04 18:59:25 +02:00
7514139d18 Add “ends here” footer
It’s useless, but required by package-lint.
2022-06-29 21:22:32 +02:00
5ff002372b Depend on Emacs >= 25.1 2022-06-29 21:22:18 +02:00
6de0e152eb Disable tab-bar by default in the PIP frame 2022-06-29 19:26:01 +02:00
68818946a6 Specify a parent customization group 2022-06-28 22:06:47 +02:00
30da3d7c08 Add headers 2022-06-28 21:57:26 +02:00
aba0e70aec Replace mapc with other constructs 2022-06-28 21:44:26 +02:00
63c390d76b Don’t prefix lambdas with #' 2022-06-28 21:41:32 +02:00
05fe647254 Add “;;; Commentary:” label 2022-06-28 21:41:13 +02:00
31037cf599 Update screenshot 2022-06-18 10:16:55 +02:00
ab090d02ee Add contact information to README 2022-06-15 19:44:32 +02:00
dbde4c1ee3 Disable minibuffer in the PIP frame by default
It looks nicer and it’s likely better to use a larger minibuffer in
another frame if needed.
2022-06-15 19:43:30 +02:00
86f6ef3abc Add support for displaying buffers in PIP temporarily 2022-05-16 07:59:32 +02:00
31636a8ccf Allow specifying a buffer to add when called from a program 2022-05-16 07:21:41 +02:00
5c89fb4fd6 Remove forgotten ‘or’ from pip-frame-add-buffer 2022-05-16 07:12:31 +02:00
a3722fbb12 Add compiled files to .gitignore 2022-05-16 07:11:40 +02:00
a0b669e25b Add missing function docstrings 2022-05-16 07:10:16 +02:00
4 changed files with 134 additions and 39 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.elc
*.eld

View File

@ -43,6 +43,26 @@ this utility:
* How?
** Installation
You can install pip-frame.el from [[https://melpa.org/#/pip-frame][MELPA]]. If you use [[https://github.com/radian-software/straight.el][straight.el]] and
prefer using source repos directly, you can install pip-frame.el as
follows:
#+begin_src elisp
(straight-use-package
'(pip-frame :type git
:repo "https://git.zamazal.org/pdm/pip-frame"
:local-repo "pip-frame"))
#+end_src
For manual installation, simply put [[./pip-frame.el][pip-frame.el]] file to your
=site-lisp= directory and add the following to your Emacs configuration:
#+begin_src elisp
(require pip-frame)
#+end_src
** Commands
The PIP frame is created using =M-x pip-frame-add-buffer= command. You
@ -96,6 +116,10 @@ Some customization examples:
(internal-border-width . 20))))
#+end_src
-----
* Contact
/To Petr./
pip-frame.el is available at [[https://git.zamazal.org/pdm/pip-frame][git.zamazal.org]]. You can file issues
there.
If you dont want to discuss things publicly or to bother registering
at yet another web site, you can reach me at [[mailto:pdm@zamazal.org][pdm@zamazal.org]].

View File

@ -1,7 +1,13 @@
;;; pip-frame.el --- PIP frame support -*- lexical-binding: t -*-
;;; pip-frame.el --- Display and manage a PIP frame -*- lexical-binding: t -*-
;; Copyright (C) 2022 Milan Zamazal <pdm@zamazal.org>
;; Author: Milan Zamazal <pdm@zamazal.org>
;; Version: 1
;; Package-Requires: ((emacs "25.1"))
;; Keywords: frames
;; URL: https://git.zamazal.org/pdm/pip-frame
;; COPYRIGHT NOTICE
;;
;; This program is free software: you can redistribute it and/or modify
@ -17,6 +23,8 @@
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Display a floating Emacs frame with selected buffers.
;; Similar to PIP on screens. Probably most useful in EXWM.
;;
@ -25,10 +33,13 @@
;; `M-x pip-frame-remove-buffer' or close the whole frame with
;; `M-x pip-frame-delete-frame'.
;;; Code:
(require 'cl-lib)
(defgroup pip-frame ()
"Display PIP frame.")
"Display PIP frame."
:group 'frames)
(defcustom pip-frame-scale 4
"How many times to shrink the PIP frame relative to the display size."
@ -49,7 +60,9 @@ Usually, the value should be larger than 0 and much smaller than 1."
(defcustom pip-frame-parameters
'((left . 0.9)
(top . 0.9))
(top . 0.9)
(tab-bar-lines . 0)
(minibuffer . nil))
"Alist of frame parameters to use for the PIP frame.
The frame size is determined automatically using `pip-frame-scale'
custom option but it can be overriden here."
@ -61,16 +74,21 @@ custom option but it can be overriden here."
:type '(alist :key-type symbol :value-type sexp)
:group 'pip-frame)
(defcustom pip-frame-temporary-buffer-seconds 10
"Number of seconds to keep a buffer displayed temporarily by default."
:type 'number
:group 'pip-frame)
(defvar pip-frame--name "PIP-frame")
(defun pip-frame--get-frame (&optional no-error)
(let ((frame (cl-find pip-frame--name (frame-list)
:key #'(lambda (f) (frame-parameter f 'name)))))
:key (lambda (f) (frame-parameter f 'name)))))
(or frame
(unless no-error
(error "No PIP frame")))))
(defun pip-frame--make-frame ()
(defun pip-frame--make-frame (buffer)
(let ((frame (make-frame `((name . ,pip-frame--name)
(unsplittable . t)
,@pip-frame-parameters
@ -79,54 +97,93 @@ custom option but it can be overriden here."
(frame-inhibit-implied-resize t)
(face-height (round (/ (face-attribute 'default :height) pip-frame-font-scale))))
(set-face-attribute 'default frame :height face-height)
(mapc #'(lambda (p) (set-face-attribute 'default frame (car p) (cdr p)))
pip-frame-face-attributes)
(dolist (p pip-frame-face-attributes)
(set-face-attribute 'default frame (car p) (cdr p)))
(set-window-buffer (car (window-list frame)) buffer)
frame))
(defun pip-frame-delete-frame ()
""
"Delete the PIP frame."
(interactive)
(delete-frame (pip-frame--get-frame)))
(defun pip-frame--buffers ()
(mapcar #'window-buffer (window-list (pip-frame--get-frame))))
(defun pip-frame--add-additional-buffer ()
(let* ((windows (window-list (pip-frame--get-frame)))
(sizes (mapcar #'(lambda (w)
(let ((width (window-body-width w t))
(height (window-body-height w t)))
(cons (+ (* width width) (* height height))
w)))
windows))
(largest (cdr (cl-first (cl-sort sizes #'> :key #'car))))
(side (if (> (window-body-width largest t) (* 2 (window-body-height largest t)))
'right
'below))
(new-window (split-window largest nil side)))
(set-window-buffer new-window (current-buffer))))
(defun pip-frame--add-additional-buffer (buffer temporary)
(let ((windows (window-list (pip-frame--get-frame))))
(unless (and temporary
(cl-find buffer windows :key #'window-buffer :test #'eq))
(let* ((sizes (mapcar (lambda (w)
(let ((width (window-body-width w t))
(height (window-body-height w t)))
(cons (+ (* width width) (* height height))
w)))
windows))
(largest (cdr (cl-first (cl-sort sizes #'> :key #'car))))
(side (if (> (window-body-width largest t)
(* 2 (window-body-height largest t)))
'right
'below))
(new-window (split-window largest nil side)))
(set-window-buffer new-window buffer)))))
(defvar pip-frame--buffer-timers '())
(defun pip-frame--make-buffer-temporary (buffer seconds)
(when (eq seconds t)
(setq seconds pip-frame-temporary-buffer-seconds))
(let ((timer (run-with-timer seconds nil #'pip-frame-remove-buffer buffer)))
(setq pip-frame--buffer-timers (cons (cons buffer timer)
pip-frame--buffer-timers))))
(defun pip-frame--delete-buffer-timer (buffer)
(let ((timer (alist-get buffer pip-frame--buffer-timers)))
(when timer
(cancel-timer timer)
(setq pip-frame--buffer-timers
(assq-delete-all buffer pip-frame--buffer-timers)))))
;;;###autoload
(defun pip-frame-add-buffer ()
""
(defun pip-frame-add-buffer (&optional buffer-or-name temporary)
"Add a buffer to the PIP frame.
If there is no PIP frame then create one.
If BUFFER-OR-NAME is specified, add the given buffer to the frame,
otherwise add the current buffer.
If TEMPORARY is given, display the buffer in the PIP frame for that
many seconds and then remove it from the frame. If t, display it for
`pip-frame-temporary-buffer-seconds'.
A buffer can be added and displayed multiple times in the frame. If
TEMPORARY is non-nil, the buffer is not displayed again if it is
already present."
(interactive)
(let ((frame (or (pip-frame--get-frame t))))
(let ((frame (pip-frame--get-frame t))
(buffer (get-buffer (or buffer-or-name (current-buffer)))))
(if frame
(pip-frame--add-additional-buffer)
(pip-frame--make-frame))))
(progn
(when temporary
(pip-frame--delete-buffer-timer buffer))
(pip-frame--add-additional-buffer buffer temporary))
(pip-frame--make-frame buffer))
(when temporary
(pip-frame--make-buffer-temporary buffer temporary))))
(defun pip-frame-remove-buffer (buffer-name)
""
(defun pip-frame-remove-buffer (buffer-or-name)
"Remove buffer named BUFFER-OR-NAME from the PIP frame.
If it is the last buffer in the PIP frame, delete the frame.
If the buffer is not present in the PIP frame, do nothing."
(interactive (list (completing-read "Remove PIP buffer: "
(mapcar #'buffer-name (pip-frame--buffers))
nil t)))
(let* ((windows (window-list (pip-frame--get-frame)))
(windows-to-delete (cl-remove buffer-name windows
:key #'(lambda (w) (buffer-name (window-buffer w)))
:test-not #'string=)))
(if (= (length windows-to-delete) (length windows))
(pip-frame-delete-frame)
(mapc #'delete-window windows-to-delete))))
(if-let ((frame (pip-frame--get-frame t))
(windows (window-list frame))
(buffer (get-buffer buffer-or-name))
(windows-to-delete (cl-remove buffer windows
:key #'window-buffer
:test-not #'eq)))
(if (= (length windows-to-delete) (length windows))
(pip-frame-delete-frame)
(seq-do #'delete-window windows-to-delete))))
(defun pip-frame--move (x y)
(let ((frame (pip-frame--get-frame)))
@ -140,18 +197,22 @@ custom option but it can be overriden here."
(round (* pip-frame-move-step (nth (if vertical 3 2) workarea))))))
(defun pip-frame-move-left ()
"Move the PIP frame left."
(interactive)
(pip-frame--move (- (pip-frame--move-step nil)) 0))
(defun pip-frame-move-right ()
"Move the PIP frame right."
(interactive)
(pip-frame--move (pip-frame--move-step nil) 0))
(defun pip-frame-move-up ()
"Move the PIP frame up."
(interactive)
(pip-frame--move 0 (- (pip-frame--move-step t))))
(defun pip-frame-move-down ()
"Move the PIP frame down."
(interactive)
(pip-frame--move 0 (pip-frame--move-step t)))
@ -164,9 +225,17 @@ custom option but it can be overriden here."
map))
(defun pip-frame-move ()
""
"Move PIP frame interactively.
Use arrow keys to move the frame around.
Any other key stops this command and executes its own command."
(interactive)
(message "Use arrow keys to move the frame, any other key to quit")
(set-transient-map pip-frame-move-map t))
(provide 'pip-frame)
;; Local Variables:
;; checkdoc-force-docstrings-flag: nil
;; End:
;;; pip-frame.el ends here

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 50 KiB