Subject: bug#28922: closed (Re: bug#28922: Pixel scrolling by frame-char-height)

From: [email-masked] (GNU bug Tracking System)
Date: Fri, 27 Oct 2017 14:25:02 +0000

Your bug report

#28922: Pixel scrolling by frame-char-height

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
28922: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=28922
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#28922: Pixel scrolling by frame-char-height
From: Eli Zaretskii <[email-masked]>
To: Tak Kunihiro <[email-masked]>
Cc: [email-masked]
Date: Fri, 27 Oct 2017 17:24:17 +0300
List-Id: <debbugs-submit.debbugs.gnu.org>

> Date: Sat, 21 Oct 2017 13:11:14 +0900 (JST)
> From: Tak Kunihiro <[email-masked]>
> Cc: [email-masked]
> 
> I'm sending a patch for lisp/pixel-scroll.el.

Thanks, I pushed it to the emacs-26 branch.

Please in the future make sure the 1st line of the doc string is not
longer than 79 characters, and is a full sentence.

Also, please fill the lines in the log entry to be shorter than 75
characters, not including the leading whitespace.  And include the bug
number in the log message.

(I modified your patch to fix all those issues.)

> diff --git a/etc/NEWS b/etc/NEWS
> old mode 100644
> new mode 100755
> index 74d6e8bf1c..d9eb4f9462
> --- a/cygdrive/c/Users/dream/Downloads/emacs/etc/NEWS
> +++ b/cygdrive/c/Users/dream/Dropbox/Downloads@TK/NEWS
> @@ -76,6 +76,12 @@ globally or for individual definitions.
>  the XTerm window title.  This feature is experimental and is disabled
>  by default.
>  
> +** Pixel-Scroll
> +
> +---
> +*** Now scroll vertically by number of pixels returned by
> +'frame-char-height' when 'pixel-resolution-fine-flag' is set to t.

I didn't install this part, because pixel-scroll-mode is a new mode
and is mentioned as such in NEWS.  So everything about it is "new",
and there's no need to mention individual changes.

Subject: Pixel scrolling by frame-char-height
From: Tak Kunihiro <[email-masked]>
To: [email-masked]
Cc: [email-masked]
Date: Sat, 21 Oct 2017 13:11:14 +0900 (JST)
List-Id: <debbugs-submit.debbugs.gnu.org>

I'm sending a patch for lisp/pixel-scroll.el.

This patch lets mouse wheel scroll vertically by number of pixels
returned by `frame-char-height' as suggested as below.

  https://lists.gnu.org/archive/html/emacs-devel/2017-09/msg00385.html

Also, by considering response of display engine as suggested as below,
this patch offers stable vertically scrolling even when window is
horizontally scrolled.

  https://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00621.html

ChangeLog entry and patch to NEWS also are attached.

* ChangeLog

2017-10-21 Tak Kunihiro <[email-masked]>

        Scroll vertically by number of pixels returned by `frame-char-height' with or without horizontally scrolled

        * lisp/pixel-scroll.el (pixel-resolution-fine-flag): When t, scroll pixels returned by `frame-char-height'.
        (pixel-scroll-up): Scroll by `frame-char-height'. Fix algorithm to move cursor to avoid unexpected jump.
        (pixel-scroll-down): Scroll by `frame-char-height'.
        (pixel-bob-at-top-p): Consider number of pixels that is about to scroll.
        (pixel-posn-y-at-point): Consider existence of an overlay string.  Return nil when horizontally scrolled.
        (pixel-point-at-top-p): Consider number of pixels that is about to scroll. Use different algorithm when horizontally scrolled.
        (pixel-point-at-bottom-p): Consider number of pixels that is about to scroll. Return nil when horizontally scrolled.
        (pixel-scroll-pixel-down): Move cursor when horizontally scrolled.
        (pixel--whistlestop-line-up): Change cosmetics and move cursor when horizontally scrolled.
        (pixel-line-height): Call `pixel-visual-line-height' instead of `line-pixel-height'.
        (pixel-visual-line-height): New function to return height in pixels of text line where cursor is with or without horizontally scrolled, considering response of display engine.
        (pixel-visible-pos-in-window): New function to return position of a char shown on text line where cursor is on screen with or without horizontally scrolled.
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
old mode 100644
new mode 100755
index 4f183addaa..e5c1dad1eb
--- a/cygdrive/c/Users/dream/Downloads/emacs/lisp/pixel-scroll.el
+++ b/cygdrive/c/Users/dream/.emacs.d/site-lisp/pixel-scroll.el
@@ -74,10 +74,13 @@
 More wait will result in slow and gentle scroll.")
 
 (defvar pixel-resolution-fine-flag nil
-  "Set scrolling resolution to a pixel instead of a line.
-After a pixel scroll, typing C-n or C-p scrolls the window to
-make it fully visible, and undoes the effect of the pixel-level
-scroll.")
+  "Set scrolling resolution to pixels instead of a line.
+When it is t, scrolling resolution is number of pixels obtained
+by `frame-char-height' instead of a line.  When it is number,
+scrolling resolution is set to number of pixels specified.  In
+case you need scrolling resolution of a pixel, set to 1.  After a
+pixel scroll, typing C-n or C-p scrolls the window to make it
+fully visible, and undoes the effect of the pixel-level scroll.")
 
 ;;;###autoload
 (define-minor-mode pixel-scroll-mode
@@ -102,13 +105,16 @@ This is an alternative of `scroll-up'.  Scope moves downward."
   (interactive)
   (or arg (setq arg 1))
   (dotimes (ii arg) ; move scope downward
-    (if (pixel-eob-at-top-p) ; when end-of-the-buffer is close
-        (scroll-up 1) ; relay on robust method
-      (when (pixel-point-at-top-p) ; prevent too late
-        (vertical-motion 1)) ; move point downward
-      (pixel-scroll-pixel-up (if pixel-resolution-fine-flag
-                                 1
-                               (pixel-line-height)))))) ; move scope downward
+    (let ((amt (if pixel-resolution-fine-flag
+                   (if (integerp pixel-resolution-fine-flag)
+                       pixel-resolution-fine-flag
+                     (frame-char-height))
+                 (pixel-line-height))))
+      (if (pixel-eob-at-top-p)            ; when end-of-the-buffer is close
+          (scroll-up 1)                   ; relay on robust method
+        (while (pixel-point-at-top-p amt) ; prevent too late (multi tries)
+          (vertical-motion 1))            ; move point downward
+        (pixel-scroll-pixel-up amt)))))   ; move scope downward
 
 (defun pixel-scroll-down (&optional arg)
   "Scroll text of selected window down ARG lines.
@@ -116,48 +122,61 @@ This is and alternative of `scroll-down'.  Scope moves upward."
   (interactive)
   (or arg (setq arg 1))
   (dotimes (ii arg)
-    (if (or (pixel-bob-at-top-p) ; when beginning-of-the-buffer is seen
-            (pixel-eob-at-top-p)) ; for file with a long line
-        (scroll-down 1) ; relay on robust method
-      (while (pixel-point-at-bottom-p) ; prevent too late (multi tries)
-        (vertical-motion -1))
-      (pixel-scroll-pixel-down (if pixel-resolution-fine-flag
-                                   1
-                                 (pixel-line-height -1))))))
-
-(defun pixel-bob-at-top-p ()
-  "Return non-nil if beginning of buffer is at top of window."
-  (equal (window-start) (point-min)))
+    (let ((amt (if pixel-resolution-fine-flag
+                   (if (integerp pixel-resolution-fine-flag)
+                       pixel-resolution-fine-flag
+                     (frame-char-height))
+                 (pixel-line-height -1))))
+      (if (or (pixel-bob-at-top-p amt) ; when beginning-of-the-buffer is seen
+              (pixel-eob-at-top-p))    ; for file with a long line
+          (scroll-down 1)              ; relay on robust method
+        (while (pixel-point-at-bottom-p amt) ; prevent too late (multi tries)
+          (vertical-motion -1))
+        (pixel-scroll-pixel-down amt)))))
+
+(defun pixel-bob-at-top-p (amt)
+  "Return non-nil if beginning of buffer is at top of window on coming scroll of AMT pixels."
+  (and (equal (window-start) (point-min))
+       (< (window-vscroll nil t) amt)))
 
 (defun pixel-eob-at-top-p ()
   "Return non-nil if end of buffer is at top of window."
   (<= (count-lines (window-start) (window-end)) 2)) ; count-screen-lines
 
 (defun pixel-posn-y-at-point ()
-  "Return y coordinates of point in pixels of current window."
-  (let ((hscroll0 (window-hscroll))
-        (y (cdr (posn-x-y (posn-at-point)))))
-    ;; when point is out of scope by hscroll
-    (unless y
-      (save-excursion
-        (set-window-hscroll nil (current-column))
-        (setq y (cdr (posn-x-y (posn-at-point))))
-        (set-window-hscroll nil hscroll0)))
-    y))
-
-(defun pixel-point-at-top-p ()
-  "Return if point is located at top of a window."
-  (let* ((y (pixel-posn-y-at-point))
-         (top-margin y))
-    (< top-margin (pixel-line-height))))
-
-(defun pixel-point-at-bottom-p ()
-  "Return if point is located at bottom of a window."
-  (let* ((y (pixel-posn-y-at-point))
-         (edges (window-inside-pixel-edges))
+  "Return y coordinates of point in pixels of current window.
+This returns nil when horizontally scrolled."
+  (when (equal (window-hscroll) 0)
+    (save-excursion
+      ;; When there's an overlay string on a line, move
+      ;; point by (beginning-of-visual-line).
+      (beginning-of-visual-line)
+      ;; (- (cadr (pos-visible-in-window-p (point) nil t))
+      ;;    (line-pixel-height))
+      (cdr (posn-x-y (posn-at-point))))))
+
+(defun pixel-point-at-top-p (amt)
+  "Return if point is located at top of a window on coming scroll of AMT pixels.
+When location of point was not obtained, this returns if point is at top of window."
+  (let ((y (pixel-posn-y-at-point))
+        top-margin)
+    (cond
+     (y
+      (setq top-margin y)
+      (< top-margin amt))
+     (t
+      (<= (count-lines (window-start) (point)) 1)))))
+
+(defun pixel-point-at-bottom-p (amt)
+  "Return if point is located at bottom of a window on coming scroll of AMT pixels.
+When location of point was not obtained, this returns nil."
+  (let* ((edges (window-inside-pixel-edges))
          (height (- (nth 3 edges) (nth 1 edges))) ; (- bottom top)
-         (bottom-margin (- height (+ y (line-pixel-height))))) ; bottom margin
-    (< bottom-margin (pixel-line-height -1)))) ; coming unseen line
+         (y (pixel-posn-y-at-point))
+         bottom-margin)
+    (when y
+      (setq bottom-margin (- height (+ y (pixel-visual-line-height))))
+      (< bottom-margin amt)))) ; coming unseen line
 
 (defun pixel-scroll-pixel-up (amt)
   "Scroll text of selected windows up AMT pixels.
@@ -173,8 +192,12 @@ Scope moves upward."
   (while (> amt 0)
     (let ((vs (window-vscroll nil t)))
       (if (equal vs 0)
-          (pixel-scroll-down-and-set-window-vscroll
-           (1- (pixel-line-height -1)))
+          (progn
+            ;; On horizontal scrolling, move cursor.
+            (when (> (window-hscroll) 0)
+              (vertical-motion -1))
+            (pixel-scroll-down-and-set-window-vscroll
+             (1- (pixel-line-height -1))))
         (set-window-vscroll nil (1- vs) t))
       (setq amt (1- amt))
       (sit-for pixel-wait))))
@@ -189,11 +212,16 @@ Scope moves downward.  This function returns number of pixels
 that was scrolled."
   (let* ((src (window-vscroll nil t))  ; EXAMPLE (initial)      @0   @8  @88
          (height (pixel-line-height))  ;                        25   25   23
-         (line (1+ (/ src height)))    ; catch up + one line    Ä1   Ä1   Ä4
+         (line (1+ (/ src height)))    ; catch up + one line     1    1    4
          (dst (* line height))         ; goal                  @25  @25  @92
          (delta (- dst src)))          ; pixels to be scrolled  25   17    4
     (pixel--whistlestop-pixel-up (1- delta)) ; until one less  @24  @24  @91
-    (scroll-up line) (sit-for pixel-wait) ; scroll 1 pixel      @0   @0   @0
+    (dotimes (ii line)
+      ;; On horizontal scrolling, move cursor.
+      (when (> (window-hscroll) 0)
+        (vertical-motion 1))
+      (scroll-up 1))
+    (sit-for pixel-wait)               ; scroll 1 pixel         @0   @0   @0
     delta))
 
 (defun pixel--whistlestop-pixel-up (n)
@@ -211,9 +239,60 @@ unseen line above the first line, respectively, is provided."
   (or pos (setq pos (window-start)))
   (when (< pos 0)
     (setq pos (pixel-point-at-unseen-line)))
-  (save-excursion
-    (goto-char pos)
-    (line-pixel-height))) ; frame-char-height
+  (let ((vs1 (window-vscroll nil t))
+        height)
+    (set-window-vscroll nil 0 t)
+    (save-excursion
+      (goto-char pos)
+      (setq height (pixel-visual-line-height))) ; line-pixel-height, frame-char-height
+    (set-window-vscroll nil vs1 t)
+    height))
+
+(defun pixel-visual-line-height ()
+  "Return height in pixels of text line where cursor is in the selected window."
+  (let ((pos (pixel-visible-pos-in-window)))
+    (cond
+     ;; When a char of line is shown, obtain height by
+     ;; (line-pixel-height).
+     (pos (save-excursion (goto-char pos) (line-pixel-height)))
+     ;; When no char of line is shown but the line is at the top,
+     ;; obtain height by (line-pixel-height).  This is based on
+     ;; expected response from display engine.  See following
+     ;; discussion.
+     ;; https://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00621.html
+     ((equal (count-lines (window-start) (point)) 1)
+      (line-pixel-height))
+     ;; No char of line is shown and the line is not at the top,
+     ;; obtain height by (frame-char-height).
+     (t (frame-char-height)))))
+
+(defun pixel-visible-pos-in-window ()
+  "Return position of a char shown on text line where cursor is in the selected window.
+This will look for positions of point and end-of-visual-line,
+then positions from beginning-of-visual-line to
+end-of-visual-line.  When no char in a line is shown, this
+returns nil."
+  (let* ((beginning-of-visual-line-pos (save-excursion (beginning-of-visual-line) (point)))
+         (end-of-visual-line-pos (save-excursion (end-of-visual-line) (point)))
+         (pos-list (number-sequence beginning-of-visual-line-pos end-of-visual-line-pos))
+         (edges (window-inside-pixel-edges))
+         (width (- (nth 2 edges) (nth 0 edges)))
+         posn-x
+         visible-pos)
+    ;; Optimize list of position to be surveyed.
+    (push end-of-visual-line-pos pos-list)
+    (push (point) pos-list)
+    (delete-dups pos-list)
+    ;; Find out a char with position X that is more than zero and less than width of screen.
+    (while (and (not visible-pos)
+                pos-list)
+      (setq posn-x (car (pos-visible-in-window-p (car pos-list) nil t)))
+      (if (and posn-x
+               (<= 0 posn-x)
+               (< posn-x width))
+          (setq visible-pos (car pos-list))
+        (setq pos-list (cdr pos-list))))
+    visible-pos))
 
 (defun pixel-point-at-unseen-line ()
   "Return the character position of line above the selected window.
diff --git a/etc/NEWS b/etc/NEWS
old mode 100644
new mode 100755
index 74d6e8bf1c..d9eb4f9462
--- a/cygdrive/c/Users/dream/Downloads/emacs/etc/NEWS
+++ b/cygdrive/c/Users/dream/Dropbox/Downloads@TK/NEWS
@@ -76,6 +76,12 @@ globally or for individual definitions.
 the XTerm window title.  This feature is experimental and is disabled
 by default.
 
+** Pixel-Scroll
+
+---
+*** Now scroll vertically by number of pixels returned by
+'frame-char-height' when 'pixel-resolution-fine-flag' is set to t.
+
 
 * New Modes and Packages in Emacs 27.1