summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-03-29 06:54:09 +0200
committercashmere <cashmere@autistici.org>2026-03-29 06:54:09 +0200
commite5ee5bb0e498c66b473e7eecc8c31e29f503d85c (patch)
treef4930dd28b4db70b61fef0d712613baced9e7a73
parent09b88bbf00bf4019ea71a893061d7f8edbf5d39f (diff)
downloadkitty-graphics-e5ee5bb0e498c66b473e7eecc8c31e29f503d85c.tar.gz
feat: heading-aware scroll and buffer-change handling
Fix scroll/buffer-switch behavior for heading overlays: - on-window-change: preserve heading overlay position caches (images clear theirs for re-placement, but headings need old→new comparison to erase multicell blocks properly) - on-buffer-change: erase heading multicell blocks when their buffer becomes hidden, preventing ghost artifacts if the new buffer's content doesn't fully overwrite the heading cells - on-buffer-change: preserve heading caches for visible buffers (same rationale as on-window-change) The existing two-phase refresh already handles scroll correctly: phase 1 detects position changes and erases moved headings, phase 2 re-emits all visible headings. These fixes ensure the cache state is correct for that comparison to work.
-rw-r--r--kitty-graphics.el36
1 files changed, 26 insertions, 10 deletions
diff --git a/kitty-graphics.el b/kitty-graphics.el
index 3f54177..4d26d04 100644
--- a/kitty-graphics.el
+++ b/kitty-graphics.el
@@ -1316,19 +1316,30 @@ then invalidates position caches and schedules a refresh."
(buffer-name))
(dolist (ov kitty-gfx--overlays)
(when (overlay-buffer ov)
- (let ((id (overlay-get ov 'kitty-gfx-id))
- (pid (overlay-get ov 'kitty-gfx-pid)))
- (if (overlay-get ov 'kitty-gfx-last-row)
- (when (and id pid)
- (kitty-gfx--delete-placement id pid))
- (kitty-gfx--log "on-buffer-change: ov pid=%s has no last-row (never placed or already cleaned)" pid)))
- (overlay-put ov 'kitty-gfx-last-row nil)
- (overlay-put ov 'kitty-gfx-last-col nil)))))))
+ (if (overlay-get ov 'kitty-gfx-heading)
+ ;; Heading overlay — erase multicell block to prevent
+ ;; ghost artifacts if the new buffer doesn't fully
+ ;; overwrite the heading's terminal cells.
+ (when (overlay-get ov 'kitty-gfx-last-row)
+ (kitty-gfx--erase-heading ov)
+ (overlay-put ov 'kitty-gfx-last-row nil)
+ (overlay-put ov 'kitty-gfx-last-col nil))
+ ;; Image overlay — delete terminal placement
+ (let ((id (overlay-get ov 'kitty-gfx-id))
+ (pid (overlay-get ov 'kitty-gfx-pid)))
+ (if (overlay-get ov 'kitty-gfx-last-row)
+ (when (and id pid)
+ (kitty-gfx--delete-placement id pid))
+ (kitty-gfx--log "on-buffer-change: ov pid=%s has no last-row (never placed or already cleaned)" pid)))
+ (overlay-put ov 'kitty-gfx-last-row nil)
+ (overlay-put ov 'kitty-gfx-last-col nil))))))))
;; Reset cache for visible buffers so they re-place correctly.
+ ;; Heading overlays preserve cache (same rationale as on-window-change).
(dolist (buf (buffer-list))
(with-current-buffer buf
(dolist (ov kitty-gfx--overlays)
- (when (overlay-buffer ov)
+ (when (and (overlay-buffer ov)
+ (not (overlay-get ov 'kitty-gfx-heading)))
(overlay-put ov 'kitty-gfx-last-row nil)
(overlay-put ov 'kitty-gfx-last-col nil)))))
;; Longer debounce: cancel any fast leading-edge cooldown and
@@ -1354,10 +1365,15 @@ buffer before settling to one)."
(setq kitty-gfx--cell-pixel-width nil
kitty-gfx--cell-pixel-height nil)
;; Reset position cache so images get re-placed at correct positions.
+ ;; Heading overlays PRESERVE their cache — the refresh cycle needs
+ ;; old→new position comparison to erase multicell blocks properly.
+ ;; Images can clear cache because re-placing with the same PID is
+ ;; idempotent (the terminal replaces the old placement).
(dolist (buf (buffer-list))
(with-current-buffer buf
(dolist (ov kitty-gfx--overlays)
- (when (overlay-buffer ov)
+ (when (and (overlay-buffer ov)
+ (not (overlay-get ov 'kitty-gfx-heading)))
(overlay-put ov 'kitty-gfx-last-row nil)
(overlay-put ov 'kitty-gfx-last-col nil)))))
;; Longer debounce: cancel any fast leading-edge cooldown and