refactor: text-scale-mode per-buffer zoom (×1.5/krok), UI stays at base size

This commit is contained in:
2026-02-22 15:26:14 +01:00
parent 196ebacd47
commit 292f257bfc
2 changed files with 68 additions and 53 deletions

119
config.el
View File

@@ -698,79 +698,96 @@ Skips past the TODO keyword and optional priority indicator [#A]."
;;; ============================================================
;;; ACCESSIBILITY — GLOBAL TEXT SCALING (SPC z)
;;; ============================================================
;; Uses `default-text-scale` for true global scaling (face remapping on all
;; frames). Unlike `text-scale-mode` this affects every buffer, minibuffer,
;; popup, which-key, corfu, and transient menu uniformly.
;; Uses per-buffer `text-scale-mode' applied to ALL buffers simultaneously.
;; Unlike `default-text-scale', this scales ONLY buffer text.
;; Modeline, minibuffer, which-key, corfu popups stay at base size
;; and remain fully visible and usable even at very high zoom levels.
;;
;; Step: ×1.5 per step (multiplicative). From 14pt base:
;; +1 → ×1.5 ≈ 21pt +2 → ×2.25 ≈ 32pt
;; +3 → ×3.4 ≈ 47pt +4 → ×5.1 ≈ 71pt
;; +5 → ×7.6 ≈ 106pt
;;
;; SPC z + / SPC z = zoom in
;; SPC z - zoom out
;; SPC z 0 reset to default (saves for restore)
;; SPC z z restore previous zoom
(use-package! default-text-scale
:config
(setq default-text-scale-amount 10)) ; 10 units = 1pt
(setq text-scale-mode-step 1.5)
(defvar my/zoom-total-delta 0
"Cumulative face-height delta applied by global zoom (in face units).")
(defvar my/zoom-steps 0
"Current global zoom step count. 0 = default, positive = bigger.")
(defvar my/zoom-saved-delta nil
"Saved delta before last reset, used by `my/zoom-restore'.")
(defvar my/zoom-saved-steps nil
"Step count saved before last `my/zoom-reset', for restore.")
(defvar my/zoom-min-height 80
"Minimum face height (in units, 80 = 8pt). Zoom out stops here.")
(defun my/zoom--apply-all (steps)
"Apply text-scale of STEPS to every live buffer."
(dolist (buf (buffer-list))
(when (buffer-live-p buf)
(with-current-buffer buf
(text-scale-set steps)))))
(defun my/zoom--current-height ()
"Return the current default face height including zoom delta."
(face-attribute 'default :height))
(defun my/zoom--auto-apply ()
"Apply current zoom to newly opened/switched buffers."
(unless (= my/zoom-steps 0)
(text-scale-set my/zoom-steps)))
(defun my/zoom--msg ()
"Display current effective font size in pt."
(let ((h (my/zoom--current-height)))
(message "Zoom: %s pt (delta %+d)"
(/ h 10)
my/zoom-total-delta)))
;; Hook: apply zoom when a new buffer gets a major mode (covers find-file,
;; new scratch buffers, magit, org-agenda, help, etc.)
(add-hook 'after-change-major-mode-hook #'my/zoom--auto-apply)
(defun my/zoom-in ()
"Increase global font size by 1 pt."
"Zoom in one step (×1.5) across all buffers."
(interactive)
(default-text-scale-increase)
(cl-incf my/zoom-total-delta default-text-scale-amount)
(my/zoom--msg))
(cl-incf my/zoom-steps)
(my/zoom--apply-all my/zoom-steps)
(message "Zoom +%d (×%.2f ≈%dpt)"
my/zoom-steps
(expt text-scale-mode-step my/zoom-steps)
(round (* 14 (expt text-scale-mode-step my/zoom-steps)))))
(defun my/zoom-out ()
"Decrease global font size by 1 pt (respects minimum)."
"Zoom out one step (÷1.5) across all buffers."
(interactive)
(let ((new-height (- (my/zoom--current-height) default-text-scale-amount)))
(if (< new-height my/zoom-min-height)
(message "Zoom: already at minimum (%d pt)" (/ (my/zoom--current-height) 10))
(default-text-scale-decrease)
(cl-decf my/zoom-total-delta default-text-scale-amount)
(my/zoom--msg))))
(cl-decf my/zoom-steps)
(my/zoom--apply-all my/zoom-steps)
(message "Zoom %d (×%.2f ≈%dpt)"
my/zoom-steps
(expt text-scale-mode-step my/zoom-steps)
(round (* 14 (expt text-scale-mode-step my/zoom-steps)))))
(defun my/zoom-reset ()
"Reset zoom to default size. Saves current delta for restore."
"Reset all buffers to default text size. Saves current step for restore."
(interactive)
(if (= my/zoom-total-delta 0)
(message "Zoom: already at default size")
(setq my/zoom-saved-delta my/zoom-total-delta)
(default-text-scale-reset)
(setq my/zoom-total-delta 0)
(message "Zoom: reset to default (saved %+d for restore)"
my/zoom-saved-delta)))
(if (= my/zoom-steps 0)
(message "Zoom: already at default")
(setq my/zoom-saved-steps my/zoom-steps)
(my/zoom--apply-all 0)
(setq my/zoom-steps 0)
(message "Zoom reset to default (was %+d — SPC z z to restore)"
my/zoom-saved-steps)))
(defun my/zoom-restore ()
"Restore the zoom level saved before last reset."
"Restore zoom level saved before last reset."
(interactive)
(if (null my/zoom-saved-delta)
(if (null my/zoom-saved-steps)
(message "Zoom: nothing to restore")
(default-text-scale-adjust my/zoom-saved-delta)
(setq my/zoom-total-delta my/zoom-saved-delta)
(setq my/zoom-saved-delta nil)
(my/zoom--msg)))
(my/zoom--apply-all my/zoom-saved-steps)
(setq my/zoom-steps my/zoom-saved-steps
my/zoom-saved-steps nil)
(message "Zoom restored: %+d (×%.2f ≈%dpt)"
my/zoom-steps
(expt text-scale-mode-step my/zoom-steps)
(round (* 14 (expt text-scale-mode-step my/zoom-steps))))))
(map! :leader
(:prefix ("z" . "zoom")
:desc "Zoom in" "+" #'my/zoom-in
:desc "Zoom in" "=" #'my/zoom-in
:desc "Zoom out" "-" #'my/zoom-out
:desc "Reset zoom" "0" #'my/zoom-reset
:desc "Restore zoom" "z" #'my/zoom-restore))
:desc "Zoom in (×1.5/krok)" "+" #'my/zoom-in
:desc "Zoom in (×1.5/krok)" "=" #'my/zoom-in
:desc "Zoom out (÷1.5/krok)" "-" #'my/zoom-out
:desc "Reset na výchozí" "0" #'my/zoom-reset
:desc "Restore předchozí zoom" "z" #'my/zoom-restore))
;;; ============================================================