summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-12 07:17:29 -0500
committerCraig Jennings <c@cjennings.net>2026-05-12 07:17:29 -0500
commitb7c6b2c59a2ad74e8e886471ea57b2e87f812d4a (patch)
tree5bd5f927bee7c42887c9317b014ba603e1e4c06a /modules
parent2ec5acd31f480064036a4567d1af5592b2d52666 (diff)
downloaddotemacs-b7c6b2c59a2ad74e8e886471ea57b2e87f812d4a.tar.gz
dotemacs-b7c6b2c59a2ad74e8e886471ea57b2e87f812d4a.zip
fix(nov): center the EPUB text by setting window margins directly
The 80% width from `4d9a206' wasn't actually narrowing the page: `cj/nov-apply-preferences' set `nov-text-width' to t (nov renders the text unfilled, one long line per paragraph) and counted on `visual-fill-column-mode' to set the window's display margins, but those margins never got applied in nov-mode buffers (even after manually re-running the layout), so the text wrapped at the full window width. The cause is still unknown. This drops `visual-fill-column' from nov entirely: - `nov-text-width' is a column count (~80% of the window's natural width), so nov's `shr' fills the text itself. - `cj/nov-update-layout' sets the window's left/right margins directly to `(natural - text-width) / 2' each, centering the block, and pushes the fringes out to the window edge so they don't show as thin lines beside the text. When the width changes it re-renders, restoring the reading position approximately. - `cj/nov-apply-preferences' adds a `kill-buffer-hook' that drops the margins and fringes when the EPUB buffer goes away, so a later buffer in that window isn't left indented. - `+'/`=' and `-'/`_' adjust `cj/nov-margin-percent' and re-flow + re-center. The text-width math moved into a `cj/nov--natural-window-width' helper alongside the existing `cj/nov--text-width'. Known nit: the centering is a touch left-of-center because shr wraps at word boundaries, so the rendered text is a bit narrower than `nov-text-width' and the right margin ends up slightly larger. Logged as a follow-up.
Diffstat (limited to 'modules')
-rw-r--r--modules/calibredb-epub-config.el92
1 files changed, 55 insertions, 37 deletions
diff --git a/modules/calibredb-epub-config.el b/modules/calibredb-epub-config.el
index d4b5092b..b8c2a9c0 100644
--- a/modules/calibredb-epub-config.el
+++ b/modules/calibredb-epub-config.el
@@ -47,9 +47,8 @@
(declare-function calibredb-find-create-search-buffer "calibredb" ())
(declare-function calibredb-search-keyword-filter "calibredb" (keyword))
(declare-function cj/open-file-with-command "system-utils" (command))
-(declare-function visual-fill-column-mode "visual-fill-column" (&optional arg))
-(declare-function visual-fill-column--adjust-window "visual-fill-column" ())
(declare-function nov-render-document "nov" ())
+(defvar nov-text-width) ; from nov.el; set buffer-local here
;; -------------------------- CalibreDB Ebook Manager --------------------------
@@ -120,12 +119,6 @@ Adjust it live with `cj/nov-widen-text' and `cj/nov-narrow-text'.")
(advice-add 'set-auto-mode :around #'cj/force-nov-mode-for-epub)
-;; Visual-fill-column provides centered text with margins
-(use-package visual-fill-column
- :defer t
- :config
- (setq-default visual-fill-column-center-text t))
-
;; Define helper functions before use-package so they're available for hooks
(defun cj/forward-paragraph-and-center ()
"Forward one paragraph and center the page."
@@ -142,33 +135,49 @@ result is at least `cj/nov-min-text-width'."
(max cj/nov-min-text-width
(floor (* text-width-ratio total-cols)))))
+(defun cj/nov--natural-window-width (&optional window)
+ "Return WINDOW's column count, adding back any display margins already set.
+WINDOW defaults to the window showing the current buffer on any frame; 80 if
+there is none. Adding the margins back makes the value stable under repeated
+layout passes -- each pass narrows the body width but not the natural width."
+ (if-let ((win (or window (get-buffer-window (current-buffer) t))))
+ (let ((m (window-margins win)))
+ (+ (window-body-width win) (or (car m) 0) (or (cdr m) 0)))
+ 80))
+
(defun cj/nov--text-width-for-window (&optional window)
- "Return preferred Nov text width for WINDOW.
-Computed from the window's natural column count -- its current body width
-plus any margins already set -- so that re-running `cj/nov-update-layout' is
-idempotent: each pass would otherwise shave the column by another margin
-fraction, since setting margins narrows the body width."
- (let* ((window (or window (get-buffer-window (current-buffer) t)))
- (margins (and window (window-margins window)))
- (natural-cols (if window
- (+ (window-body-width window)
- (or (car margins) 0)
- (or (cdr margins) 0))
- 80)))
- (cj/nov--text-width natural-cols)))
+ "Return the preferred EPUB text column count for WINDOW."
+ (cj/nov--text-width (cj/nov--natural-window-width window)))
(defun cj/nov-update-layout (&optional _frame)
- "Recalculate Nov's centered text column (width + margins) for this buffer.
-Also runs from `window-configuration-change-hook' and
-`window-size-change-functions' to stay responsive to splits and resizes."
+ "Size the EPUB text column for this buffer and center it in its window.
+`nov-text-width' is set so nov's `shr' fills the text to roughly 80% of the
+window, and the window's display margins are set to center that block (no
+`visual-fill-column' -- its margin-setting wasn't taking effect in nov-mode).
+When the width changes the document is re-rendered, restoring the reading
+position approximately. Runs from `window-configuration-change-hook' and is a
+command."
(interactive)
(when (derived-mode-p 'nov-mode)
- (when (require 'visual-fill-column nil t)
- (setq-local visual-fill-column-center-text t)
- (setq-local visual-fill-column-width (cj/nov--text-width-for-window))
- (visual-fill-column-mode 1)
- (when (bound-and-true-p visual-fill-column-mode)
- (visual-fill-column--adjust-window)))))
+ (let* ((win (get-buffer-window (current-buffer) t))
+ (total (cj/nov--natural-window-width win))
+ (width (cj/nov--text-width total)))
+ (unless (eql nov-text-width width)
+ (setq-local nov-text-width width)
+ (let ((frac (when (> (point-max) (point-min))
+ (/ (float (- (point) (point-min)))
+ (- (point-max) (point-min))))))
+ (nov-render-document)
+ (when frac
+ (goto-char (+ (point-min)
+ (round (* frac (- (point-max) (point-min)))))))))
+ (when win
+ ;; floor: never let the margins squeeze the text area below WIDTH.
+ (let ((margin (max 0 (/ (- total width) 2))))
+ (set-window-margins win margin margin))
+ ;; Push the fringes out to the window's edge; otherwise they sit between
+ ;; the margin and the text and show as thin vertical lines beside it.
+ (set-window-fringes win nil nil t)))))
(defun cj/--nov-adjust-margin (delta)
"Add DELTA to `cj/nov-margin-percent' (clamped 0..25), re-lay-out, and report.
@@ -197,19 +206,28 @@ A positive DELTA narrows the text column; a negative DELTA widens it."
(face-remap-add-relative 'variable-pitch :family "Merriweather" :height 1.0 :foreground "#E8DCC0")
(face-remap-add-relative 'default :family "Merriweather" :height 180 :foreground "#E8DCC0")
(face-remap-add-relative 'fixed-pitch :height 180 :foreground "#E8DCC0")
- ;; Make this buffer-local so other Nov buffers can choose differently
- ;; Setting to t makes nov respect visual-fill-column margins
- (setq-local nov-text-width t)
;; Enable visual-line-mode for proper text wrapping
(visual-line-mode 1)
;; Set fill-column as a fallback
(setq-local fill-column 100)
- ;; Enable visual-fill-column for centered text with margins
+ ;; Have nov's `shr' fill the text to a column count, and `cj/nov-update-layout'
+ ;; center it with the window's display margins. This deliberately does NOT
+ ;; use `visual-fill-column' -- its margin-setting wasn't taking effect here.
+ (setq-local nov-text-width (cj/nov--text-width-for-window))
(cj/nov-update-layout)
- ;; Keep centered text width responsive after splits/resizes.
+ ;; Keep the width and centering responsive to splits/resizes.
(add-hook 'window-configuration-change-hook #'cj/nov-update-layout nil t)
- ;; Re-render so the first page is laid out inside the margins just set;
- ;; nov-mode's initial render ran before `nov-text-width'/visual-fill-column.
+ ;; Drop the centering margins from the window when this EPUB buffer goes away,
+ ;; so a later buffer in the same window isn't left indented.
+ (add-hook 'kill-buffer-hook
+ (lambda ()
+ (when-let ((w (get-buffer-window (current-buffer))))
+ (set-window-margins w nil)
+ (set-window-fringes w nil)))
+ nil t)
+ ;; Render once now; the window may not exist yet (so this uses the fallback
+ ;; width), and `cj/nov-update-layout' on the first window-config change
+ ;; re-flows at the real width.
(nov-render-document))
(defun cj/nov-open-external ()