4

I was looking for a replacement to scroll-up / scroll-down that operates in pixel units.

  • pixel-scroll-mode animates the scroll, I didn't see any functions that apply an offset directly.
  • set-window-vscroll

    • Uses absolute position.
    • It needs the scroll to be split into pixels & characters
    • It doesn't handle moving the cursor.
    • Moving the cursor afterwards resets the scroll position.

    So it seems like it's lower level than a scroll operation.

Is there a function where you can run:

(scroll-down-by-pixels 40)

To scroll which handles constraining the point by scroll margin for eg?

ideasman42
  • 8,375
  • 1
  • 28
  • 105

2 Answers2

1

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)))
ideasman42
  • 8,375
  • 1
  • 28
  • 105
0

How about (require 'pixel-scroll) and then use pixel-scroll-pixel-up ("Scroll text of selected windows up AMT pixels.") and pixel-scroll-pixel-down ("Scroll text of selected windows down AMT pixels.")?

lawlist
  • 18,826
  • 5
  • 37
  • 118
  • This doesn't clamp the cursor position like regular scroll does (when scrolling up, down works), testing, sometimes it resets the scroll location in a strange way too - maybe related to the cursor being out of the view. – ideasman42 Jul 13 '19 at 02:42
  • Perhaps you might be interested in `centered-cursor-mode`: https://github.com/emacsmirror/centered-cursor-mode – lawlist Jul 13 '19 at 02:46
  • I'm writing my own mode, so depending on another mode just to clamp the cursor seems overkill. I would like to have the behavior of scroll up/down, just with pixel offsets. – ideasman42 Jul 13 '19 at 02:52