diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-27 14:36:57 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-27 14:36:57 -0500 |
| commit | 84d8ab6a3ad97697a717a770c42010e9ec9076e5 (patch) | |
| tree | 3abf00cdbce3e04b4afb22cb80b346b9e2eeeb0b /modules | |
| parent | 8635f7d2221f92763247e2330070f1c0796d23d7 (diff) | |
| download | dotemacs-84d8ab6a3ad97697a717a770c42010e9ec9076e5.tar.gz dotemacs-84d8ab6a3ad97697a717a770c42010e9ec9076e5.zip | |
feat(window): remember a side window's size across toggles
The F10 music playlist opened at a fixed fraction every time, so any manual resize was lost the moment it was toggled closed. Now the toggle captures the window's size on close and reopens at that height, for the rest of the session.
The mechanism is generic, not music-specific. cj/window-size-fraction (geometry-lib) is the pure kernel: a clamped window/frame ratio. cj/side-window-capture-size and cj/side-window-display (toggle-lib) wrap it for any display-buffer-in-side-window consumer — height for top/bottom, width for left/right — storing the remembered fraction in a caller-supplied state var. It mirrors the direction-split toggle pattern the vterm dispatchers already use, but for atomic side windows that can't be split.
music-config wires F10 to it: cj/music-playlist-window-height is the default, cj/--music-playlist-height holds the remembered value (in-memory, resets each session).
12 new tests across the two libs, Normal/Boundary/Error each covered.
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/cj-window-geometry-lib.el | 17 | ||||
| -rw-r--r-- | modules/cj-window-toggle-lib.el | 45 | ||||
| -rw-r--r-- | modules/music-config.el | 20 |
3 files changed, 80 insertions, 2 deletions
diff --git a/modules/cj-window-geometry-lib.el b/modules/cj-window-geometry-lib.el index 1c1ab9fd..cc638f76 100644 --- a/modules/cj-window-geometry-lib.el +++ b/modules/cj-window-geometry-lib.el @@ -112,5 +112,22 @@ a fresh half." (not (window-in-direction (nth 1 perp) w)))) (window-list (or frame (selected-frame)) 'never))))) +(defun cj/window-size-fraction (window-size frame-size &optional min-frac max-frac) + "Return WINDOW-SIZE as a fraction of FRAME-SIZE, clamped to [MIN-FRAC, MAX-FRAC]. + +WINDOW-SIZE and FRAME-SIZE are line or column counts. MIN-FRAC and +MAX-FRAC default to 0.05 and 0.95: a side window pinned to either extreme +is almost certainly a mistake, and a 0.0 fraction makes +`display-buffer-in-side-window' unusable. Returns nil when FRAME-SIZE is +not a positive number, or WINDOW-SIZE is not a number, so a caller can +fall back to its own default instead of dividing by zero. + +This is the kernel for remembering a side window's user-resized size: capture +the fraction at toggle-off, replay it on the next toggle-on." + (when (and (numberp window-size) (numberp frame-size) (> frame-size 0)) + (let ((lo (or min-frac 0.05)) + (hi (or max-frac 0.95))) + (max lo (min hi (/ (float window-size) frame-size)))))) + (provide 'cj-window-geometry-lib) ;;; cj-window-geometry-lib.el ends here diff --git a/modules/cj-window-toggle-lib.el b/modules/cj-window-toggle-lib.el index 6021c2eb..ef57e5cf 100644 --- a/modules/cj-window-toggle-lib.el +++ b/modules/cj-window-toggle-lib.el @@ -81,5 +81,50 @@ placement; the remaining alist entries are passed through." filtered))) (display-buffer-in-direction buffer effective))) +;; --------------------------- side-window helpers --------------------------- +;; +;; A second, simpler pattern for `display-buffer-in-side-window' consumers. +;; Side windows are atomic (they can't be split), so there's no direction to +;; capture -- only a size on the side's axis. These helpers remember that +;; size across toggles: capture the user's mouse-resized size at toggle-off, +;; replay it on the next toggle-on. The remembered value is held in the +;; consumer's own state variable (passed by symbol) and is in-memory only. + +(defun cj/side-window-capture-size (window side size-var) + "Write WINDOW's size on SIDE's axis, as a frame fraction, into SIZE-VAR. + +SIDE is a `display-buffer-in-side-window' side symbol: left or right +capture width, top or bottom capture height. SIZE-VAR is the symbol of the +consumer's stored-fraction variable; it receives the captured value via +`set'. Capturing at toggle-off and replaying on the next toggle-on is what +makes a manual mouse-resize stick for the rest of the session. + +No-op when WINDOW is nil or not live, or when the fraction can't be computed +\(see `cj/window-size-fraction') -- SIZE-VAR keeps its prior value so the +caller's default still applies." + (when (window-live-p window) + (let* ((horizontal (memq side '(left right))) + (frame (window-frame window)) + (win-size (if horizontal + (window-total-width window) + (window-total-height window))) + (frame-size (if horizontal (frame-width frame) (frame-height frame))) + (frac (cj/window-size-fraction win-size frame-size))) + (when frac (set size-var frac))))) + +(defun cj/side-window-display (buffer side size-var default-size) + "Display BUFFER in a SIDE side window at the remembered or DEFAULT-SIZE size. + +SIDE is a `display-buffer-in-side-window' side symbol. SIZE-VAR is the +symbol of the consumer's stored-fraction variable; its value is used when +bound and non-nil, otherwise DEFAULT-SIZE. The size lands under +`window-width' for a left/right side and `window-height' for top/bottom. +Returns the displayed window (or nil if display fails)." + (let* ((stored (and (boundp size-var) (symbol-value size-var))) + (size (or stored default-size)) + (size-key (if (memq side '(left right)) 'window-width 'window-height))) + (display-buffer-in-side-window + buffer (list (cons 'side side) (cons size-key size))))) + (provide 'cj-window-toggle-lib) ;;; cj-window-toggle-lib.el ends here diff --git a/modules/music-config.el b/modules/music-config.el index 197c73be..fd619d8c 100644 --- a/modules/music-config.el +++ b/modules/music-config.el @@ -94,6 +94,7 @@ (require 'subr-x) (require 'user-constants) (require 'keybindings) ;; provides cj/custom-keymap +(require 'cj-window-toggle-lib) ;; side-window size memory (F10 toggle) ;;; Settings (no Customize) @@ -513,20 +514,35 @@ Intended for use on `emms-player-finished-hook'." ) +(defvar cj/music-playlist-window-height 0.3 + "Default fraction of frame height for the F10 music playlist side window. +Used until the playlist is resized and toggled off this session; after that, +the toggled-off height is remembered in `cj/--music-playlist-height'.") + +(defvar cj/--music-playlist-height nil + "Last height fraction the playlist side window was toggled off at. +nil means fall back to `cj/music-playlist-window-height'. In-memory only -- +resets each Emacs session.") + (defun cj/music-playlist-toggle () - "Toggle the EMMS playlist buffer in a bottom side window." + "Toggle the EMMS playlist buffer in a bottom side window. +The window opens at `cj/music-playlist-window-height'; if it has been +resized and toggled off this session, it reopens at that remembered height." (interactive) (let* ((buf-name cj/music-playlist-buffer-name) (buffer (get-buffer buf-name)) (win (and buffer (get-buffer-window buffer)))) (if win (progn + (cj/side-window-capture-size win 'bottom 'cj/--music-playlist-height) (delete-window win) (message "Playlist window closed")) (progn (cj/emms--setup) (setq buffer (cj/music--ensure-playlist-buffer)) - (setq win (display-buffer-in-side-window buffer '((side . bottom) (window-height . 0.5)))) + (setq win (cj/side-window-display + buffer 'bottom 'cj/--music-playlist-height + cj/music-playlist-window-height)) (select-window win) (with-current-buffer buffer (if (and (fboundp 'emms-playlist-current-selected-track) |
