There are some constraints on pixel scrolling.
- You can't use
scroll-up
as it tags the window to reset the pixel scrolling value.
- Instead you need to use
set-window-start
, with the 3rd NOFORCE
argument to t
.
- This complicates things because the
point
then needs to be moved before the window, since, unlike scroll, setting the window-start doesn't move the point to stay in the window bounds.
Here is a function that handles all this.
;; Per-line Scroll.
;; return remainder of lines to scroll (matching forward-line).
(defun my-scroll-by-lines (window lines also-move-point)
"Line based scroll that optionally move the point.
Argument WINDOW The window to scroll.
Argument LINES The number of lines to scroll (signed).
Argument ALSO-MOVE-POINT When non-nil, move the POINT as well."
(let ((lines-remainder 0))
(when also-move-point
(let ((lines-point-remainder (forward-line lines)))
(unless (zerop lines-point-remainder)
(setq lines (- lines lines-point-remainder)))))
(unless (zerop lines)
(set-window-start
window
(save-excursion
(goto-char (window-start))
(setq lines-remainder (forward-line lines))
(point))
t)
(when also-move-point
(unless (zerop lines-remainder)
(forward-line (- lines-remainder)))))
lines-remainder))
;; Per-pixel Scroll.
;; return remainder of lines to scroll (matching forward-line).
(defun my-scroll-by-pixels (window char-height delta-px also-move-point)
"Line based scroll that optionally move the point.
Argument WINDOW The window to scroll.
Argument CHAR-HEIGHT The result of `frame-char-height'.
Argument DELTA-PX The number of pixels to scroll (signed).
Argument ALSO-MOVE-POINT When non-nil, move the POINT as well."
(cond
((< delta-px 0)
(let*
(
(scroll-px-prev (- char-height (window-vscroll nil t))) ;; flip.
(scroll-px-next (+ scroll-px-prev (- delta-px))) ;; flip.
(lines (/ scroll-px-next char-height))
(scroll-px (- scroll-px-next (* lines char-height)))
(lines-remainder 0))
(unless (zerop lines)
(setq lines-remainder
(- (my-scroll-by-lines window (- lines) also-move-point))) ;; flip
(unless (zerop lines-remainder)
(setq scroll-px char-height)))
(set-window-vscroll window (- char-height scroll-px) t)
(- lines-remainder)))
((> delta-px 0)
(let*
(
(scroll-px-prev (window-vscroll nil t))
(scroll-px-next (+ scroll-px-prev delta-px))
(lines (/ scroll-px-next char-height))
(scroll-px (- scroll-px-next (* lines char-height)))
(lines-remainder 0))
(unless (zerop lines)
(setq lines-remainder
(my-scroll-by-lines window lines also-move-point))
(unless (zerop lines-remainder)
(setq scroll-px char-height)))
(set-window-vscroll window scroll-px t)
lines-remainder))
;; no lines scrolled.
(t 0)))