replace golden-ratio (unmaintained) with zoom (maintained, more features)

This commit is contained in:
KemoNine 2022-09-03 16:43:01 -04:00
parent 671149706b
commit 61c1847b97
9 changed files with 709 additions and 227 deletions

View File

@ -0,0 +1,54 @@
;;; zoom-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "zoom" "zoom.el" (0 0 0 0))
;;; Generated autoloads from zoom.el
(defvar zoom-mode nil "\
Non-nil if Zoom mode is enabled.
See the `zoom-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `zoom-mode'.")
(custom-autoload 'zoom-mode "zoom" nil)
(autoload 'zoom-mode "zoom" "\
Perform `zoom' automatically as the selected window changes.
This is a minor mode. If called interactively, toggle the `Zoom
mode' mode. If the prefix argument is positive, enable the mode,
and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `(default-value \\='zoom-mode)'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\(fn &optional ARG)" t nil)
(autoload 'zoom "zoom" "\
Zoom the current window and balance the others according to `zoom-size'." t nil)
(register-definition-prefixes "zoom" '("zoom-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; zoom-autoloads.el ends here

View File

@ -0,0 +1,2 @@
;;; Generated package description from zoom.el -*- no-byte-compile: t -*-
(define-package "zoom" "20220411.1126" "Fixed and automatic balanced window layout" '((emacs "24.4")) :commit "2104abb074682db79b9ff3a748e8e2e760a4d8cf" :authors '(("Andrea Cardaci" . "cyrus.and@gmail.com")) :maintainer '("Andrea Cardaci" . "cyrus.and@gmail.com") :keywords '("frames") :url "https://github.com/cyrus-and/zoom")

View File

@ -0,0 +1,295 @@
;;; zoom.el --- Fixed and automatic balanced window layout
;; Copyright (c) 2022 Andrea Cardaci <cyrus.and@gmail.com>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.
;; Author: Andrea Cardaci <cyrus.and@gmail.com>
;; Version: 0.2.4
;; Package-Version: 20220411.1126
;; Package-Commit: 2104abb074682db79b9ff3a748e8e2e760a4d8cf
;; URL: https://github.com/cyrus-and/zoom
;; Package-Requires: ((emacs "24.4"))
;; Keywords: frames
;;; Commentary:
;; This minor mode takes care of managing the window sizes by enforcing a fixed
;; and automatic balanced layout where the currently selected window is resized
;; according to `zoom-size' which can be an absolute value in lines/columns, a
;; ratio between the selected window and frame size or even a custom callback.
;;; Code:
(defgroup zoom nil
"Enforce a fixed and automatic balanced window layout."
:group 'windows)
(defcustom zoom-size '(80 . 24)
"Size hint for the selected window.
It can be either a cons or a function.
Each component of the cons can be either an absolute value in
lines/columns or a ratio between the selected window and the
frame size. In the former case the window is resized according
to its body size, i.e., the total window size can be much larger.
In any case, windows are never shrinked if they are already
larger than the resulting size.
The function takes no arguments and returns a cons as specified
above."
:type '(choice (function :tag "Custom")
(cons :tag "Fixed"
(choice (integer :tag "Columns")
(float :tag "Width ratio"))
(choice (integer :tag "Lines")
(float :tag "Height ratio"))))
:safe #'consp
:group 'zoom)
(defcustom zoom-ignored-major-modes nil
"List of ignored major modes.
Selected windows using any of these major modes should not be
enlarged (only balanced)."
:type '(repeat symbol)
:group 'zoom)
(defcustom zoom-ignored-buffer-names nil
"List of ignored buffer names.
Selected windows displaying any of these buffers should not be
enlarged (only balanced)."
:type '(repeat string)
:group 'zoom)
(defcustom zoom-ignored-buffer-name-regexps nil
"List of ignored buffer name regexps.
Selected windows displaying buffers matching any of these regexps
should not be enlarged (only balanced)."
:type '(repeat regexp)
:group 'zoom)
(defcustom zoom-ignore-predicates nil
"List of additional predicates that allow to ignore windows.
These functions are called (in order) to decide whether the
selected window should be ignored (only balanced) or not.
Predicates take no parameters and as soon as one function returns
a non-nil value, the selected window is ignored and the others
are not called."
:type '(repeat function)
:group 'zoom)
(defcustom zoom-minibuffer-preserve-layout t
"Non-nil means that the layout is retained when the minubuffer is entered.
Otherwise, since the minibuffer cannot be zoomed, all the other
windows are simply balanced. Setting this variable to nil can be
useful when third-party modes use the minibuffer to display more
than few lines."
:type 'boolean
:group 'zoom)
;;;###autoload
(define-minor-mode zoom-mode
"Perform `zoom' automatically as the selected window changes."
:global t
:lighter " Z"
:require 'zoom
(if zoom-mode
(zoom--on)
(zoom--off)))
;;;###autoload
(defun zoom ()
"Zoom the current window and balance the others according to `zoom-size'."
(interactive)
;; manual invocation only works when this mode is disabled
(if zoom-mode
(message "Window zooming is automatic (M-x zoom-mode to disable)")
(zoom--update)))
(defun zoom--on ()
"Enable hooks and advices and update the layout."
;; register the zoom handler
(add-function :after pre-redisplay-function #'zoom--handler)
;; disable mouse resizing
(advice-add #'mouse-drag-mode-line :override #'ignore)
(advice-add #'mouse-drag-vertical-line :override #'ignore)
(advice-add #'mouse-drag-header-line :override #'ignore)
;; update the layout once loaded
(dolist (frame (frame-list))
(with-selected-frame frame
(zoom--handler))))
(defun zoom--off ()
"Disable hooks and advices and evenly balance the windows."
;; unregister the zoom handler
(remove-function pre-redisplay-function #'zoom--handler)
;; enable mouse resizing
(advice-remove #'mouse-drag-mode-line #'ignore)
(advice-remove #'mouse-drag-vertical-line #'ignore)
(advice-remove #'mouse-drag-header-line #'ignore)
;; leave with a clean layout
(dolist (frame (frame-list))
(balance-windows frame)))
(defvar zoom--last-window nil
"Keep track of the currently selected window.")
(defun zoom--get-frame-snapshot ()
"Get a snapshot of the current frame.
The return value is used to determine if an update is needed."
;; the windows size is added to update the layout during the frame resize (XXX
;; note that the simple frame size is not enough because for some reason it's
;; not properly updated during maximize/restore operations); the mouse
;; tracking invalidates the condition so it is possible to delay the update
;; during a mouse tracking event; the list is converted to string to also
;; compare the buffer (the order of the list places the selected window first)
;; TODO adding the window sizes here causes one spurious update because first
;; the selected window is changed then the resize happens
(format "%s" (list (default-value 'track-mouse)
(mapcar (lambda (window) (list window
(window-total-width)
(window-total-height)))
(window-list)))))
(defun zoom--handler (&optional ignored)
"Handle an update event.
Argument IGNORED is ignored."
;; check if the windows have changed since the last time
(let ((snapshot (zoom--get-frame-snapshot)))
(unless (equal (frame-parameter nil 'zoom--frame-snapshot) snapshot)
;; update the windows snapshot
(set-frame-parameter nil 'zoom--frame-snapshot snapshot)
;; zoom the previously selected window if a mouse tracking is in progress
;; of if the minibuffer is selected (according to the user preference)
(with-selected-window
(if (or (equal (selected-window) zoom--last-window)
(and zoom-minibuffer-preserve-layout (window-minibuffer-p))
(default-value 'track-mouse))
zoom--last-window
;; XXX this can't be simply omitted because it's needed to address
;; the case where a window changes buffer from/to a ignored buffer
(selected-window))
;; update the currently zoomed window
(setq zoom--last-window (selected-window))
(zoom--update)))))
(defun zoom--update ()
"Update the window layout in the current frame."
(let (;; temporarily disables this mode during resize to avoid infinite
;; recursion (probably not needed thanks to the following)
(zoom-mode nil)
;; temporarily disable all (even external) hooks about window
;; configuration changes to try to avoid potential flickering since
;; `balance-windows' calls them
(window-configuration-change-hook nil)
;; make sure that other windows are resized nicely after resizing the
;; selected one
(window-combination-resize t)
;; make sure that the exact same amount of pixels is assigned to all the
;; siblings
(window-resize-pixelwise t))
;; start from a balanced layout anyway
(balance-windows)
;; check if the selected window is not ignored
(unless (zoom--window-ignored-p)
(zoom--resize)
;; fix the scrolling but not for image buffers
(unless (derived-mode-p 'image-mode)
(zoom--fix-scroll)))))
(defun zoom--window-ignored-p ()
"Check whether the selected window will be ignored or not."
(or
;; `one-window-p' does not work well with the completion buffer
;; when emacsclient is used
(frame-root-window-p (selected-window))
;; never attempt to zoom the minibuffer
(window-minibuffer-p)
;; check against the major mode
(member major-mode zoom-ignored-major-modes)
;; check against the buffer name
(member (buffer-name) zoom-ignored-buffer-names)
;; check against the buffer name (using a regexp)
(catch 'ignored
(dolist (regex zoom-ignored-buffer-name-regexps)
(when (string-match regex (buffer-name))
(throw 'ignored t))))
;; check user-defined predicates
(catch 'ignored
(dolist (predicate zoom-ignore-predicates)
(when (funcall predicate)
(throw 'ignored t))))))
(defun zoom--resize ()
"Resize the selected window according to the user preference."
(let ((size-hint-cons
;; either use the cons as is or call the custom function
(if (functionp zoom-size) (funcall zoom-size) zoom-size)))
(zoom--resize-one-dimension size-hint-cons t)
(zoom--resize-one-dimension size-hint-cons nil)))
(defun zoom--resize-one-dimension (size-hint-cons horizontal)
"Resize one dimension of the selected window according to the user preference.
Argument SIZE-HINT-CONS is the size hint provided by the user.
Argument HORIZONTAL determines whether the window should be
resized horizontally or vertically."
(let* ((size-hint
(if horizontal (car size-hint-cons) (cdr size-hint-cons)))
(frame-size
(if horizontal (frame-width) (frame-height)))
;; use the total size (including fringes, scroll bars, etc.) for ratios
;; and the body size for absolute values
(window-size
(if (floatp size-hint)
(if horizontal (window-total-width) (window-total-height))
(if horizontal (window-body-width) (window-body-height))))
;; either use an absolute value or a ratio
(min-window-size
(if (floatp size-hint) (round (* size-hint frame-size)) size-hint))
;; do not shrink the window if it is already large enough
(desired-delta (max (- min-window-size window-size) 0))
;; fall back to the maximum available if the windows are too small
(delta (window-resizable nil desired-delta horizontal)))
;; actually resize the window
(window-resize nil delta horizontal)))
(defun zoom--fix-scroll ()
"Fix the horizontal scrolling if needed."
;; scroll all the way to the left border
(scroll-right (window-hscroll))
;; if the window is not wide enough to contain the point scroll to center
;; unless lines are not truncated
(when (and truncate-lines
(> (current-column) (- (window-body-width) hscroll-margin)))
(scroll-left (- (current-column) (/ (window-body-width) 2)))))
(provide 'zoom)
;;; zoom.el ends here

View File

@ -60,7 +60,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; additional packages
(add-to-list 'package-selected-packages
'(auctex-latexmk auctex json-mode python-mode powershell rust-mode origami go-mode yaml-mode dockerfile-mode lua-mode rainbow-mode rainbow-delimiters markdown-mode)
'(zoom auctex-latexmk auctex json-mode python-mode powershell rust-mode origami go-mode yaml-mode dockerfile-mode lua-mode rainbow-mode rainbow-delimiters markdown-mode)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -8,7 +8,7 @@
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
(package-initialize) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
(add-to-list 'package-selected-packages
'(popwin dired-single diredfl xclip doominhibitinhibit-modeline magit helpful helm helm-org helm-ls-git projectile helm-projectile dired-rainbow dired-rainbow-listing dired-single dash s origami persp-mode persp-mode-projectile-bridge modus-themes transpose-frame use-package)
'(zoom popwin dired-single diredfl xclip doominhibitinhibit-modeline magit helpful helm helm-org helm-ls-git projectile helm-projectile dired-rainbow dired-rainbow-listing dired-single dash s origami persp-mode persp-mode-projectile-bridge modus-themes transpose-frame use-package)
)
(require 'use-package)
@ -404,15 +404,11 @@ position between last non-whitespace and `end-of-line'."
(global-set-key (kbd "C-x w") 'flyspell-correct-wrapper)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; golden-ratio
(load "~/.emacs.d.profiles/common/golden-ratio.el")
(require 'golden-ratio)
(setq golden-ratio--value 1.618) ; default: 1.618
;; add ace-window jump to trigger commands
;;(nconc golden-ratio-extra-commands '(ace-window))
;(setq golden-ratio-exclude-modes '(org-mode))
;;(setq golden-ratio-auto-scale t)
(golden-ratio-mode t)
; zoom (replaces golden-ratio)
(require 'zoom)
(setq zoom-size '(0.618 . 0.618))
(zoom-mode t)
(global-set-key (kbd "C-x +") 'zoom)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; quick reference notes as a pop up window

View File

@ -1,216 +0,0 @@
;;; golden-ratio.el --- Automatic resizing of Emacs windows to the golden ratio
;; Copyright (C) 2012 Roman Gonzalez
;; Author: Roman Gonzalez <romanandreg@gmail.com>
;; Mantainer: Roman Gonzalez <romanandreg@gmail.com>
;; Created: 13 Oct 2012
;; Keywords: Window Resizing
;; Version: 0.0.4
;; Code inspired by ideas from Tatsuhiro Ujihisa
;; This file is not part of GNU Emacs.
;; This file is free software (MIT License)
;;; Code:
(eval-when-compile (require 'cl))
(defconst golden-ratio--value 1.618
"The golden ratio value itself.")
(defgroup golden-ratio nil
"Resize windows to golden ratio."
:group 'windows)
;; Major modes that are exempt from being resized. An example of this
;; for users of Org-mode might be:
;; ("calendar-mode") or (calendar-mode)
(defcustom golden-ratio-exclude-modes nil
"A list of symbols or strings naming major modes.
Switching to a buffer whose major mode is a member of this list
will not cause the window to be resized to the golden ratio."
:type '(repeat (choice symbol string))
:group 'golden-ratio)
;; Buffer names that are exempt from being resized. An example of this
;; for users of Org-mode might be (note the leading spaces):
;; (" *Org tags*" " *Org todo*")
(defcustom golden-ratio-exclude-buffer-names nil
"An array of strings containing buffer names.
Switching to a buffer whose name is a member of this list
will not cause the window to be resized to the golden ratio."
:type '(repeat string)
:group 'golden-ratio)
(defcustom golden-ratio-inhibit-functions nil
"List of functions to call with no arguments.
Switching to a buffer, if any of these functions returns non-nil
will not cause the window to be resized to the golden ratio."
:group 'golden-ratio
:type '(repeat symbol))
(defcustom golden-ratio-extra-commands
'(windmove-left windmove-right windmove-down windmove-up)
"List of extra commands used to jump to other window."
:group 'golden-ratio
:type '(repeat symbol))
(defcustom golden-ratio-recenter nil
"Recenter window vertically and scroll right when non--nil."
:group 'golden-ratio
:type 'boolean)
(defcustom golden-ratio-adjust-factor 1.0
"Adjust the width sizing by some factor. 1 is no adjustment.
For very wide screens/frames, ie. 3400px, .4 may work well."
:group 'golden-ratio
:type 'integer)
(defcustom golden-ratio-wide-adjust-factor 0.8
"Width adjustment factor for widescreens. Used when
toggling between widescreen and regular modes."
:group 'golden-ratio
:type 'float)
(defcustom golden-ratio-auto-scale nil
"Automatic width adjustment factoring. Scales the width
of the screens to be smaller as the frame gets bigger."
:group 'golden-ratio
:type 'boolean)
(defcustom golden-ratio-max-width nil
"Set a maximum column width on the active window."
:group 'golden-ratio
:type 'integer)
(defcustom golden-ratio-exclude-buffer-regexp nil
"A list of regexp's used to match buffer names.
Switching to a buffer whose name matches one of these regexps
will prevent the window to be resized to the golden ratio."
:type '(repeat string)
:group 'golden-ratio)
;;; Compatibility
;;
(unless (fboundp 'window-resizable-p)
(defalias 'window-resizable-p 'window--resizable-p))
(defun golden-ratio-toggle-widescreen ()
(interactive)
(if (= golden-ratio-adjust-factor 1)
(setq golden-ratio-adjust-factor golden-ratio-wide-adjust-factor)
(setq golden-ratio-adjust-factor 1))
(golden-ratio))
(defun golden-ratio-adjust (a)
"set the adjustment of window widths."
(interactive
(list
(read-number "Screeen width adjustment factor: " golden-ratio-adjust-factor)))
(setq golden-ratio-adjust-factor a)
(golden-ratio))
(defun golden-ratio--scale-factor ()
(if golden-ratio-auto-scale
(- 1.0 (* (/ (- (frame-width) 100.0) 1000.0) 1.8))
golden-ratio-adjust-factor))
(defun golden-ratio--dimensions ()
(list (floor (/ (frame-height) golden-ratio--value))
(let ((width (floor (* (/ (frame-width) golden-ratio--value)
(golden-ratio--scale-factor)))))
(if golden-ratio-max-width
(min golden-ratio-max-width width)
width))))
(defun golden-ratio--resize-window (dimensions &optional window)
(with-selected-window (or window (selected-window))
(let ((nrow (floor (- (first dimensions) (window-height))))
(ncol (floor (- (second dimensions) (window-width)))))
(when (window-resizable-p (selected-window) nrow)
(enlarge-window nrow))
(when (window-resizable-p (selected-window) ncol t)
(enlarge-window ncol t)))))
(defun golden-ratio-exclude-major-mode-p ()
"Returns non-nil if `major-mode' should not use golden-ratio."
(or (memq major-mode golden-ratio-exclude-modes)
(member (symbol-name major-mode)
golden-ratio-exclude-modes)))
;;;###autoload
(defun golden-ratio (&optional arg)
"Resizes current window to the golden-ratio's size specs."
(interactive "p")
(unless (or (and (not golden-ratio-mode) (null arg))
(window-minibuffer-p)
(one-window-p)
(golden-ratio-exclude-major-mode-p)
(member (buffer-name)
golden-ratio-exclude-buffer-names)
(and golden-ratio-exclude-buffer-regexp
(loop for r in golden-ratio-exclude-buffer-regexp
thereis (string-match r (buffer-name))))
(and golden-ratio-inhibit-functions
(loop for fun in golden-ratio-inhibit-functions
thereis (funcall fun))))
(let ((dims (golden-ratio--dimensions))
(golden-ratio-mode nil))
;; Always disable `golden-ratio-mode' to avoid
;; infinite loop in `balance-windows'.
(let (window-configuration-change-hook)
(balance-windows)
(golden-ratio--resize-window dims)
(when golden-ratio-recenter
(scroll-right) (recenter)))
(run-hooks 'window-configuration-change-hook))))
;; Should return nil
(defadvice other-window
(after golden-ratio-resize-window)
(golden-ratio) nil)
;; Should return the buffer
(defadvice pop-to-buffer
(around golden-ratio-resize-window)
(prog1 ad-do-it (golden-ratio)))
(defun golden-ratio--post-command-hook ()
(when (or (memq this-command golden-ratio-extra-commands)
(and (consp this-command) ; A lambda form.
(loop for com in golden-ratio-extra-commands
thereis (or (member com this-command)
(member (car-safe com) this-command)))))
;; This is needed in emacs-25 to avoid this error from `recenter':
;; `recenter'ing a window that does not display current-buffer.
;; This doesn't happen in emacs-24.4 and previous versions.
(run-with-idle-timer 0.01 nil (lambda () (golden-ratio)))))
(defun golden-ratio--mouse-leave-buffer-hook ()
(run-at-time 0.1 nil (lambda ()
(golden-ratio))))
;;;###autoload
(define-minor-mode golden-ratio-mode
"Enable automatic window resizing with golden ratio."
:lighter " Golden"
:global t
(if golden-ratio-mode
(progn
(add-hook 'window-configuration-change-hook 'golden-ratio)
(add-hook 'post-command-hook 'golden-ratio--post-command-hook)
(add-hook 'mouse-leave-buffer-hook 'golden-ratio--mouse-leave-buffer-hook)
(ad-activate 'other-window)
(ad-activate 'pop-to-buffer))
(remove-hook 'window-configuration-change-hook 'golden-ratio)
(remove-hook 'post-command-hook 'golden-ratio--post-command-hook)
(remove-hook 'mouse-leave-buffer-hook 'golden-ratio--mouse-leave-buffer-hook)
(ad-deactivate 'other-window)
(ad-deactivate 'pop-to-buffer)))
(provide 'golden-ratio)
;;; golden-ratio.el ends here

View File

@ -0,0 +1,54 @@
;;; zoom-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
;;
;;; Code:
(add-to-list 'load-path (directory-file-name
(or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "zoom" "zoom.el" (0 0 0 0))
;;; Generated autoloads from zoom.el
(defvar zoom-mode nil "\
Non-nil if Zoom mode is enabled.
See the `zoom-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `zoom-mode'.")
(custom-autoload 'zoom-mode "zoom" nil)
(autoload 'zoom-mode "zoom" "\
Perform `zoom' automatically as the selected window changes.
This is a minor mode. If called interactively, toggle the `Zoom
mode' mode. If the prefix argument is positive, enable the mode,
and if it is zero or negative, disable the mode.
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
the mode if ARG is nil, omitted, or is a positive number.
Disable the mode if ARG is a negative number.
To check whether the minor mode is enabled in the current buffer,
evaluate `(default-value \\='zoom-mode)'.
The mode's hook is called both when the mode is enabled and when
it is disabled.
\(fn &optional ARG)" t nil)
(autoload 'zoom "zoom" "\
Zoom the current window and balance the others according to `zoom-size'." t nil)
(register-definition-prefixes "zoom" '("zoom-"))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; coding: utf-8
;; End:
;;; zoom-autoloads.el ends here

View File

@ -0,0 +1,2 @@
;;; Generated package description from zoom.el -*- no-byte-compile: t -*-
(define-package "zoom" "20220411.1126" "Fixed and automatic balanced window layout" '((emacs "24.4")) :commit "2104abb074682db79b9ff3a748e8e2e760a4d8cf" :authors '(("Andrea Cardaci" . "cyrus.and@gmail.com")) :maintainer '("Andrea Cardaci" . "cyrus.and@gmail.com") :keywords '("frames") :url "https://github.com/cyrus-and/zoom")

View File

@ -0,0 +1,295 @@
;;; zoom.el --- Fixed and automatic balanced window layout
;; Copyright (c) 2022 Andrea Cardaci <cyrus.and@gmail.com>
;;
;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; of this software and associated documentation files (the "Software"), to deal
;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.
;; Author: Andrea Cardaci <cyrus.and@gmail.com>
;; Version: 0.2.4
;; Package-Version: 20220411.1126
;; Package-Commit: 2104abb074682db79b9ff3a748e8e2e760a4d8cf
;; URL: https://github.com/cyrus-and/zoom
;; Package-Requires: ((emacs "24.4"))
;; Keywords: frames
;;; Commentary:
;; This minor mode takes care of managing the window sizes by enforcing a fixed
;; and automatic balanced layout where the currently selected window is resized
;; according to `zoom-size' which can be an absolute value in lines/columns, a
;; ratio between the selected window and frame size or even a custom callback.
;;; Code:
(defgroup zoom nil
"Enforce a fixed and automatic balanced window layout."
:group 'windows)
(defcustom zoom-size '(80 . 24)
"Size hint for the selected window.
It can be either a cons or a function.
Each component of the cons can be either an absolute value in
lines/columns or a ratio between the selected window and the
frame size. In the former case the window is resized according
to its body size, i.e., the total window size can be much larger.
In any case, windows are never shrinked if they are already
larger than the resulting size.
The function takes no arguments and returns a cons as specified
above."
:type '(choice (function :tag "Custom")
(cons :tag "Fixed"
(choice (integer :tag "Columns")
(float :tag "Width ratio"))
(choice (integer :tag "Lines")
(float :tag "Height ratio"))))
:safe #'consp
:group 'zoom)
(defcustom zoom-ignored-major-modes nil
"List of ignored major modes.
Selected windows using any of these major modes should not be
enlarged (only balanced)."
:type '(repeat symbol)
:group 'zoom)
(defcustom zoom-ignored-buffer-names nil
"List of ignored buffer names.
Selected windows displaying any of these buffers should not be
enlarged (only balanced)."
:type '(repeat string)
:group 'zoom)
(defcustom zoom-ignored-buffer-name-regexps nil
"List of ignored buffer name regexps.
Selected windows displaying buffers matching any of these regexps
should not be enlarged (only balanced)."
:type '(repeat regexp)
:group 'zoom)
(defcustom zoom-ignore-predicates nil
"List of additional predicates that allow to ignore windows.
These functions are called (in order) to decide whether the
selected window should be ignored (only balanced) or not.
Predicates take no parameters and as soon as one function returns
a non-nil value, the selected window is ignored and the others
are not called."
:type '(repeat function)
:group 'zoom)
(defcustom zoom-minibuffer-preserve-layout t
"Non-nil means that the layout is retained when the minubuffer is entered.
Otherwise, since the minibuffer cannot be zoomed, all the other
windows are simply balanced. Setting this variable to nil can be
useful when third-party modes use the minibuffer to display more
than few lines."
:type 'boolean
:group 'zoom)
;;;###autoload
(define-minor-mode zoom-mode
"Perform `zoom' automatically as the selected window changes."
:global t
:lighter " Z"
:require 'zoom
(if zoom-mode
(zoom--on)
(zoom--off)))
;;;###autoload
(defun zoom ()
"Zoom the current window and balance the others according to `zoom-size'."
(interactive)
;; manual invocation only works when this mode is disabled
(if zoom-mode
(message "Window zooming is automatic (M-x zoom-mode to disable)")
(zoom--update)))
(defun zoom--on ()
"Enable hooks and advices and update the layout."
;; register the zoom handler
(add-function :after pre-redisplay-function #'zoom--handler)
;; disable mouse resizing
(advice-add #'mouse-drag-mode-line :override #'ignore)
(advice-add #'mouse-drag-vertical-line :override #'ignore)
(advice-add #'mouse-drag-header-line :override #'ignore)
;; update the layout once loaded
(dolist (frame (frame-list))
(with-selected-frame frame
(zoom--handler))))
(defun zoom--off ()
"Disable hooks and advices and evenly balance the windows."
;; unregister the zoom handler
(remove-function pre-redisplay-function #'zoom--handler)
;; enable mouse resizing
(advice-remove #'mouse-drag-mode-line #'ignore)
(advice-remove #'mouse-drag-vertical-line #'ignore)
(advice-remove #'mouse-drag-header-line #'ignore)
;; leave with a clean layout
(dolist (frame (frame-list))
(balance-windows frame)))
(defvar zoom--last-window nil
"Keep track of the currently selected window.")
(defun zoom--get-frame-snapshot ()
"Get a snapshot of the current frame.
The return value is used to determine if an update is needed."
;; the windows size is added to update the layout during the frame resize (XXX
;; note that the simple frame size is not enough because for some reason it's
;; not properly updated during maximize/restore operations); the mouse
;; tracking invalidates the condition so it is possible to delay the update
;; during a mouse tracking event; the list is converted to string to also
;; compare the buffer (the order of the list places the selected window first)
;; TODO adding the window sizes here causes one spurious update because first
;; the selected window is changed then the resize happens
(format "%s" (list (default-value 'track-mouse)
(mapcar (lambda (window) (list window
(window-total-width)
(window-total-height)))
(window-list)))))
(defun zoom--handler (&optional ignored)
"Handle an update event.
Argument IGNORED is ignored."
;; check if the windows have changed since the last time
(let ((snapshot (zoom--get-frame-snapshot)))
(unless (equal (frame-parameter nil 'zoom--frame-snapshot) snapshot)
;; update the windows snapshot
(set-frame-parameter nil 'zoom--frame-snapshot snapshot)
;; zoom the previously selected window if a mouse tracking is in progress
;; of if the minibuffer is selected (according to the user preference)
(with-selected-window
(if (or (equal (selected-window) zoom--last-window)
(and zoom-minibuffer-preserve-layout (window-minibuffer-p))
(default-value 'track-mouse))
zoom--last-window
;; XXX this can't be simply omitted because it's needed to address
;; the case where a window changes buffer from/to a ignored buffer
(selected-window))
;; update the currently zoomed window
(setq zoom--last-window (selected-window))
(zoom--update)))))
(defun zoom--update ()
"Update the window layout in the current frame."
(let (;; temporarily disables this mode during resize to avoid infinite
;; recursion (probably not needed thanks to the following)
(zoom-mode nil)
;; temporarily disable all (even external) hooks about window
;; configuration changes to try to avoid potential flickering since
;; `balance-windows' calls them
(window-configuration-change-hook nil)
;; make sure that other windows are resized nicely after resizing the
;; selected one
(window-combination-resize t)
;; make sure that the exact same amount of pixels is assigned to all the
;; siblings
(window-resize-pixelwise t))
;; start from a balanced layout anyway
(balance-windows)
;; check if the selected window is not ignored
(unless (zoom--window-ignored-p)
(zoom--resize)
;; fix the scrolling but not for image buffers
(unless (derived-mode-p 'image-mode)
(zoom--fix-scroll)))))
(defun zoom--window-ignored-p ()
"Check whether the selected window will be ignored or not."
(or
;; `one-window-p' does not work well with the completion buffer
;; when emacsclient is used
(frame-root-window-p (selected-window))
;; never attempt to zoom the minibuffer
(window-minibuffer-p)
;; check against the major mode
(member major-mode zoom-ignored-major-modes)
;; check against the buffer name
(member (buffer-name) zoom-ignored-buffer-names)
;; check against the buffer name (using a regexp)
(catch 'ignored
(dolist (regex zoom-ignored-buffer-name-regexps)
(when (string-match regex (buffer-name))
(throw 'ignored t))))
;; check user-defined predicates
(catch 'ignored
(dolist (predicate zoom-ignore-predicates)
(when (funcall predicate)
(throw 'ignored t))))))
(defun zoom--resize ()
"Resize the selected window according to the user preference."
(let ((size-hint-cons
;; either use the cons as is or call the custom function
(if (functionp zoom-size) (funcall zoom-size) zoom-size)))
(zoom--resize-one-dimension size-hint-cons t)
(zoom--resize-one-dimension size-hint-cons nil)))
(defun zoom--resize-one-dimension (size-hint-cons horizontal)
"Resize one dimension of the selected window according to the user preference.
Argument SIZE-HINT-CONS is the size hint provided by the user.
Argument HORIZONTAL determines whether the window should be
resized horizontally or vertically."
(let* ((size-hint
(if horizontal (car size-hint-cons) (cdr size-hint-cons)))
(frame-size
(if horizontal (frame-width) (frame-height)))
;; use the total size (including fringes, scroll bars, etc.) for ratios
;; and the body size for absolute values
(window-size
(if (floatp size-hint)
(if horizontal (window-total-width) (window-total-height))
(if horizontal (window-body-width) (window-body-height))))
;; either use an absolute value or a ratio
(min-window-size
(if (floatp size-hint) (round (* size-hint frame-size)) size-hint))
;; do not shrink the window if it is already large enough
(desired-delta (max (- min-window-size window-size) 0))
;; fall back to the maximum available if the windows are too small
(delta (window-resizable nil desired-delta horizontal)))
;; actually resize the window
(window-resize nil delta horizontal)))
(defun zoom--fix-scroll ()
"Fix the horizontal scrolling if needed."
;; scroll all the way to the left border
(scroll-right (window-hscroll))
;; if the window is not wide enough to contain the point scroll to center
;; unless lines are not truncated
(when (and truncate-lines
(> (current-column) (- (window-body-width) hscroll-margin)))
(scroll-left (- (current-column) (/ (window-body-width) 2)))))
(provide 'zoom)
;;; zoom.el ends here