diff options
| author | Tak Kunihiro | 2017-10-27 17:17:42 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2017-10-27 17:17:42 +0300 |
| commit | 1bda71ec3b11eeb4d06c3da094a3cb21bac18d5c (patch) | |
| tree | 9d6e5bfe1aae832d05796608a4e8469d72cd25ff | |
| parent | 196106d37d085da6d7bf4a36dfda70f7073b2bb8 (diff) | |
| download | emacs-1bda71ec3b11eeb4d06c3da094a3cb21bac18d5c.tar.gz emacs-1bda71ec3b11eeb4d06c3da094a3cb21bac18d5c.zip | |
Improve pixel-scroll-mode
Scroll vertically by number of pixels returned by
'frame-char-height' with or without horizontally scrolled.
(Bug#28922)
* lisp/pixel-scroll.el (pixel-resolution-fine-flag): When t, scroll
by number of 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.
| -rw-r--r-- | lisp/pixel-scroll.el | 188 |
1 files changed, 135 insertions, 53 deletions
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el index 4f183addaa0..2213a0239db 100644 --- a/lisp/pixel-scroll.el +++ b/lisp/pixel-scroll.el | |||
| @@ -74,10 +74,13 @@ | |||
| 74 | More wait will result in slow and gentle scroll.") | 74 | More wait will result in slow and gentle scroll.") |
| 75 | 75 | ||
| 76 | (defvar pixel-resolution-fine-flag nil | 76 | (defvar pixel-resolution-fine-flag nil |
| 77 | "Set scrolling resolution to a pixel instead of a line. | 77 | "Set scrolling resolution to pixels instead of a line. |
| 78 | After a pixel scroll, typing C-n or C-p scrolls the window to | 78 | When it is t, scrolling resolution is number of pixels obtained |
| 79 | make it fully visible, and undoes the effect of the pixel-level | 79 | by `frame-char-height' instead of a line. When it is number, |
| 80 | scroll.") | 80 | scrolling resolution is set to number of pixels specified. In |
| 81 | case you need scrolling resolution of a pixel, set to 1. After a | ||
| 82 | pixel scroll, typing \\[next-line] or \\[previous-line] scrolls the window to make it | ||
| 83 | fully visible, and undoes the effect of the pixel-level scroll.") | ||
| 81 | 84 | ||
| 82 | ;;;###autoload | 85 | ;;;###autoload |
| 83 | (define-minor-mode pixel-scroll-mode | 86 | (define-minor-mode pixel-scroll-mode |
| @@ -102,13 +105,16 @@ This is an alternative of `scroll-up'. Scope moves downward." | |||
| 102 | (interactive) | 105 | (interactive) |
| 103 | (or arg (setq arg 1)) | 106 | (or arg (setq arg 1)) |
| 104 | (dotimes (ii arg) ; move scope downward | 107 | (dotimes (ii arg) ; move scope downward |
| 105 | (if (pixel-eob-at-top-p) ; when end-of-the-buffer is close | 108 | (let ((amt (if pixel-resolution-fine-flag |
| 106 | (scroll-up 1) ; relay on robust method | 109 | (if (integerp pixel-resolution-fine-flag) |
| 107 | (when (pixel-point-at-top-p) ; prevent too late | 110 | pixel-resolution-fine-flag |
| 108 | (vertical-motion 1)) ; move point downward | 111 | (frame-char-height)) |
| 109 | (pixel-scroll-pixel-up (if pixel-resolution-fine-flag | 112 | (pixel-line-height)))) |
| 110 | 1 | 113 | (if (pixel-eob-at-top-p) ; when end-of-the-buffer is close |
| 111 | (pixel-line-height)))))) ; move scope downward | 114 | (scroll-up 1) ; relay on robust method |
| 115 | (while (pixel-point-at-top-p amt) ; prevent too late (multi tries) | ||
| 116 | (vertical-motion 1)) ; move point downward | ||
| 117 | (pixel-scroll-pixel-up amt))))) ; move scope downward | ||
| 112 | 118 | ||
| 113 | (defun pixel-scroll-down (&optional arg) | 119 | (defun pixel-scroll-down (&optional arg) |
| 114 | "Scroll text of selected window down ARG lines. | 120 | "Scroll text of selected window down ARG lines. |
| @@ -116,48 +122,63 @@ This is and alternative of `scroll-down'. Scope moves upward." | |||
| 116 | (interactive) | 122 | (interactive) |
| 117 | (or arg (setq arg 1)) | 123 | (or arg (setq arg 1)) |
| 118 | (dotimes (ii arg) | 124 | (dotimes (ii arg) |
| 119 | (if (or (pixel-bob-at-top-p) ; when beginning-of-the-buffer is seen | 125 | (let ((amt (if pixel-resolution-fine-flag |
| 120 | (pixel-eob-at-top-p)) ; for file with a long line | 126 | (if (integerp pixel-resolution-fine-flag) |
| 121 | (scroll-down 1) ; relay on robust method | 127 | pixel-resolution-fine-flag |
| 122 | (while (pixel-point-at-bottom-p) ; prevent too late (multi tries) | 128 | (frame-char-height)) |
| 123 | (vertical-motion -1)) | 129 | (pixel-line-height -1)))) |
| 124 | (pixel-scroll-pixel-down (if pixel-resolution-fine-flag | 130 | (if (or (pixel-bob-at-top-p amt) ; when beginning-of-the-buffer is seen |
| 125 | 1 | 131 | (pixel-eob-at-top-p)) ; for file with a long line |
| 126 | (pixel-line-height -1)))))) | 132 | (scroll-down 1) ; relay on robust method |
| 127 | 133 | (while (pixel-point-at-bottom-p amt) ; prevent too late (multi tries) | |
| 128 | (defun pixel-bob-at-top-p () | 134 | (vertical-motion -1)) |
| 129 | "Return non-nil if beginning of buffer is at top of window." | 135 | (pixel-scroll-pixel-down amt))))) |
| 130 | (equal (window-start) (point-min))) | 136 | |
| 137 | (defun pixel-bob-at-top-p (amt) | ||
| 138 | "Return non-nil if window-start is at beginning of the current buffer. | ||
| 139 | Window must be vertically scrolled by not more than AMT pixels." | ||
| 140 | (and (equal (window-start) (point-min)) | ||
| 141 | (< (window-vscroll nil t) amt))) | ||
| 131 | 142 | ||
| 132 | (defun pixel-eob-at-top-p () | 143 | (defun pixel-eob-at-top-p () |
| 133 | "Return non-nil if end of buffer is at top of window." | 144 | "Return non-nil if end of buffer is at top of window." |
| 134 | (<= (count-lines (window-start) (window-end)) 2)) ; count-screen-lines | 145 | (<= (count-lines (window-start) (window-end)) 2)) ; count-screen-lines |
| 135 | 146 | ||
| 136 | (defun pixel-posn-y-at-point () | 147 | (defun pixel-posn-y-at-point () |
| 137 | "Return y coordinates of point in pixels of current window." | 148 | "Return y coordinates of point in pixels of current window. |
| 138 | (let ((hscroll0 (window-hscroll)) | 149 | This returns nil when horizontally scrolled." |
| 139 | (y (cdr (posn-x-y (posn-at-point))))) | 150 | (when (equal (window-hscroll) 0) |
| 140 | ;; when point is out of scope by hscroll | 151 | (save-excursion |
| 141 | (unless y | 152 | ;; When there's an overlay string on a line, move |
| 142 | (save-excursion | 153 | ;; point by (beginning-of-visual-line). |
| 143 | (set-window-hscroll nil (current-column)) | 154 | (beginning-of-visual-line) |
| 144 | (setq y (cdr (posn-x-y (posn-at-point)))) | 155 | ;; (- (cadr (pos-visible-in-window-p (point) nil t)) |
| 145 | (set-window-hscroll nil hscroll0))) | 156 | ;; (line-pixel-height)) |
| 146 | y)) | 157 | (cdr (posn-x-y (posn-at-point)))))) |
| 147 | 158 | ||
| 148 | (defun pixel-point-at-top-p () | 159 | (defun pixel-point-at-top-p (amt) |
| 149 | "Return if point is located at top of a window." | 160 | "Return if point is located at top of a window on coming scroll of AMT pixels. |
| 150 | (let* ((y (pixel-posn-y-at-point)) | 161 | When location of point was not obtained, this returns if point is at top |
| 151 | (top-margin y)) | 162 | of window." |
| 152 | (< top-margin (pixel-line-height)))) | 163 | (let ((y (pixel-posn-y-at-point)) |
| 153 | 164 | top-margin) | |
| 154 | (defun pixel-point-at-bottom-p () | 165 | (cond |
| 155 | "Return if point is located at bottom of a window." | 166 | (y |
| 156 | (let* ((y (pixel-posn-y-at-point)) | 167 | (setq top-margin y) |
| 157 | (edges (window-inside-pixel-edges)) | 168 | (< top-margin amt)) |
| 169 | (t | ||
| 170 | (<= (count-lines (window-start) (point)) 1))))) | ||
| 171 | |||
| 172 | (defun pixel-point-at-bottom-p (amt) | ||
| 173 | "Return if point is located at bottom of window on coming scroll of AMT pixels. | ||
| 174 | When location of point was not obtained, this returns nil." | ||
| 175 | (let* ((edges (window-inside-pixel-edges)) | ||
| 158 | (height (- (nth 3 edges) (nth 1 edges))) ; (- bottom top) | 176 | (height (- (nth 3 edges) (nth 1 edges))) ; (- bottom top) |
| 159 | (bottom-margin (- height (+ y (line-pixel-height))))) ; bottom margin | 177 | (y (pixel-posn-y-at-point)) |
| 160 | (< bottom-margin (pixel-line-height -1)))) ; coming unseen line | 178 | bottom-margin) |
| 179 | (when y | ||
| 180 | (setq bottom-margin (- height (+ y (pixel-visual-line-height)))) | ||
| 181 | (< bottom-margin amt)))) ; coming unseen line | ||
| 161 | 182 | ||
| 162 | (defun pixel-scroll-pixel-up (amt) | 183 | (defun pixel-scroll-pixel-up (amt) |
| 163 | "Scroll text of selected windows up AMT pixels. | 184 | "Scroll text of selected windows up AMT pixels. |
| @@ -173,8 +194,12 @@ Scope moves upward." | |||
| 173 | (while (> amt 0) | 194 | (while (> amt 0) |
| 174 | (let ((vs (window-vscroll nil t))) | 195 | (let ((vs (window-vscroll nil t))) |
| 175 | (if (equal vs 0) | 196 | (if (equal vs 0) |
| 176 | (pixel-scroll-down-and-set-window-vscroll | 197 | (progn |
| 177 | (1- (pixel-line-height -1))) | 198 | ;; On horizontal scrolling, move cursor. |
| 199 | (when (> (window-hscroll) 0) | ||
| 200 | (vertical-motion -1)) | ||
| 201 | (pixel-scroll-down-and-set-window-vscroll | ||
| 202 | (1- (pixel-line-height -1)))) | ||
| 178 | (set-window-vscroll nil (1- vs) t)) | 203 | (set-window-vscroll nil (1- vs) t)) |
| 179 | (setq amt (1- amt)) | 204 | (setq amt (1- amt)) |
| 180 | (sit-for pixel-wait)))) | 205 | (sit-for pixel-wait)))) |
| @@ -189,11 +214,16 @@ Scope moves downward. This function returns number of pixels | |||
| 189 | that was scrolled." | 214 | that was scrolled." |
| 190 | (let* ((src (window-vscroll nil t)) ; EXAMPLE (initial) @0 @8 @88 | 215 | (let* ((src (window-vscroll nil t)) ; EXAMPLE (initial) @0 @8 @88 |
| 191 | (height (pixel-line-height)) ; 25 25 23 | 216 | (height (pixel-line-height)) ; 25 25 23 |
| 192 | (line (1+ (/ src height))) ; catch up + one line Ä1 Ä1 Ä4 | 217 | (line (1+ (/ src height))) ; catch up + one line 1 1 4 |
| 193 | (dst (* line height)) ; goal @25 @25 @92 | 218 | (dst (* line height)) ; goal @25 @25 @92 |
| 194 | (delta (- dst src))) ; pixels to be scrolled 25 17 4 | 219 | (delta (- dst src))) ; pixels to be scrolled 25 17 4 |
| 195 | (pixel--whistlestop-pixel-up (1- delta)) ; until one less @24 @24 @91 | 220 | (pixel--whistlestop-pixel-up (1- delta)) ; until one less @24 @24 @91 |
| 196 | (scroll-up line) (sit-for pixel-wait) ; scroll 1 pixel @0 @0 @0 | 221 | (dotimes (ii line) |
| 222 | ;; On horizontal scrolling, move cursor. | ||
| 223 | (when (> (window-hscroll) 0) | ||
| 224 | (vertical-motion 1)) | ||
| 225 | (scroll-up 1)) | ||
| 226 | (sit-for pixel-wait) ; scroll 1 pixel @0 @0 @0 | ||
| 197 | delta)) | 227 | delta)) |
| 198 | 228 | ||
| 199 | (defun pixel--whistlestop-pixel-up (n) | 229 | (defun pixel--whistlestop-pixel-up (n) |
| @@ -211,9 +241,61 @@ unseen line above the first line, respectively, is provided." | |||
| 211 | (or pos (setq pos (window-start))) | 241 | (or pos (setq pos (window-start))) |
| 212 | (when (< pos 0) | 242 | (when (< pos 0) |
| 213 | (setq pos (pixel-point-at-unseen-line))) | 243 | (setq pos (pixel-point-at-unseen-line))) |
| 214 | (save-excursion | 244 | (let ((vs1 (window-vscroll nil t)) |
| 215 | (goto-char pos) | 245 | height) |
| 216 | (line-pixel-height))) ; frame-char-height | 246 | (set-window-vscroll nil 0 t) |
| 247 | (save-excursion | ||
| 248 | (goto-char pos) | ||
| 249 | (setq height (pixel-visual-line-height))) ; line-pixel-height, frame-char-height | ||
| 250 | (set-window-vscroll nil vs1 t) | ||
| 251 | height)) | ||
| 252 | |||
| 253 | (defun pixel-visual-line-height () | ||
| 254 | "Return height in pixels of text line where cursor is in the selected window." | ||
| 255 | (let ((pos (pixel-visible-pos-in-window))) | ||
| 256 | (cond | ||
| 257 | ;; When a char of line is shown, obtain height by | ||
| 258 | ;; (line-pixel-height). | ||
| 259 | (pos (save-excursion (goto-char pos) (line-pixel-height))) | ||
| 260 | ;; When no char of line is shown but the line is at the top, | ||
| 261 | ;; obtain height by (line-pixel-height). This is based on | ||
| 262 | ;; expected response from display engine. See following | ||
| 263 | ;; discussion. | ||
| 264 | ;; https://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00621.html | ||
| 265 | ((equal (count-lines (window-start) (point)) 1) | ||
| 266 | (line-pixel-height)) | ||
| 267 | ;; No char of line is shown and the line is not at the top, | ||
| 268 | ;; obtain height by (frame-char-height). | ||
| 269 | (t (frame-char-height))))) | ||
| 270 | |||
| 271 | (defun pixel-visible-pos-in-window () | ||
| 272 | "Return position shown on text line where cursor is in the selected window. | ||
| 273 | This will look for positions of point and end-of-visual-line, | ||
| 274 | then positions from beginning-of-visual-line to | ||
| 275 | end-of-visual-line. When no char in a line is shown, this | ||
| 276 | returns nil." | ||
| 277 | (let* ((beginning-of-visual-line-pos (save-excursion (beginning-of-visual-line) (point))) | ||
| 278 | (end-of-visual-line-pos (save-excursion (end-of-visual-line) (point))) | ||
| 279 | (pos-list (number-sequence beginning-of-visual-line-pos end-of-visual-line-pos)) | ||
| 280 | (edges (window-inside-pixel-edges)) | ||
| 281 | (width (- (nth 2 edges) (nth 0 edges))) | ||
| 282 | posn-x | ||
| 283 | visible-pos) | ||
| 284 | ;; Optimize list of position to be surveyed. | ||
| 285 | (push end-of-visual-line-pos pos-list) | ||
| 286 | (push (point) pos-list) | ||
| 287 | (delete-dups pos-list) | ||
| 288 | ;; Find out a char with position X that is more than zero and less | ||
| 289 | ;; than width of screen. | ||
| 290 | (while (and (not visible-pos) | ||
| 291 | pos-list) | ||
| 292 | (setq posn-x (car (pos-visible-in-window-p (car pos-list) nil t))) | ||
| 293 | (if (and posn-x | ||
| 294 | (<= 0 posn-x) | ||
| 295 | (< posn-x width)) | ||
| 296 | (setq visible-pos (car pos-list)) | ||
| 297 | (setq pos-list (cdr pos-list)))) | ||
| 298 | visible-pos)) | ||
| 217 | 299 | ||
| 218 | (defun pixel-point-at-unseen-line () | 300 | (defun pixel-point-at-unseen-line () |
| 219 | "Return the character position of line above the selected window. | 301 | "Return the character position of line above the selected window. |