aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTak Kunihiro2017-10-27 17:17:42 +0300
committerEli Zaretskii2017-10-27 17:17:42 +0300
commit1bda71ec3b11eeb4d06c3da094a3cb21bac18d5c (patch)
tree9d6e5bfe1aae832d05796608a4e8469d72cd25ff
parent196106d37d085da6d7bf4a36dfda70f7073b2bb8 (diff)
downloademacs-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.el188
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 @@
74More wait will result in slow and gentle scroll.") 74More 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.
78After a pixel scroll, typing C-n or C-p scrolls the window to 78When it is t, scrolling resolution is number of pixels obtained
79make it fully visible, and undoes the effect of the pixel-level 79by `frame-char-height' instead of a line. When it is number,
80scroll.") 80scrolling resolution is set to number of pixels specified. In
81case you need scrolling resolution of a pixel, set to 1. After a
82pixel scroll, typing \\[next-line] or \\[previous-line] scrolls the window to make it
83fully 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.
139Window 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)) 149This 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)) 161When location of point was not obtained, this returns if point is at top
151 (top-margin y)) 162of 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.
174When 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
189that was scrolled." 214that 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.
273This will look for positions of point and end-of-visual-line,
274then positions from beginning-of-visual-line to
275end-of-visual-line. When no char in a line is shown, this
276returns 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.