diff options
| author | Noam Postavsky | 2017-02-02 21:35:51 -0500 |
|---|---|---|
| committer | Noam Postavsky | 2017-02-02 21:35:51 -0500 |
| commit | ce88155d83ba84e84321ed69a39c82f40117dd1f (patch) | |
| tree | b56e2f89a597cb407747639c02333dde7e4552ba | |
| parent | 604724e49d7b44dc663ad941998a0a44aa4fc178 (diff) | |
| parent | da515a0d8e97d89a1c7e60faea190174a8c72618 (diff) | |
| download | emacs-ce88155d83ba84e84321ed69a39c82f40117dd1f.tar.gz emacs-ce88155d83ba84e84321ed69a39c82f40117dd1f.zip | |
; Merge: fixes and updates to scroll margin (Bug#5718)
- add new option `maximum-sroll-margin'
- refactor and fix scroll margin calculation
| -rw-r--r-- | doc/emacs/display.texi | 12 | ||||
| -rw-r--r-- | doc/lispref/windows.texi | 15 | ||||
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/cus-start.el | 1 | ||||
| -rw-r--r-- | src/dispextern.h | 1 | ||||
| -rw-r--r-- | src/window.c | 53 | ||||
| -rw-r--r-- | src/window.h | 2 | ||||
| -rw-r--r-- | src/xdisp.c | 104 | ||||
| -rw-r--r-- | test/manual/scroll-tests.el | 130 |
9 files changed, 243 insertions, 80 deletions
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index c6e990d9082..15c700892bc 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi | |||
| @@ -285,13 +285,17 @@ multiple variables, the order of priority is: | |||
| 285 | @code{scroll-up-aggressively} / @code{scroll-down-aggressively}. | 285 | @code{scroll-up-aggressively} / @code{scroll-down-aggressively}. |
| 286 | 286 | ||
| 287 | @vindex scroll-margin | 287 | @vindex scroll-margin |
| 288 | @vindex maximum-scroll-margin | ||
| 288 | The variable @code{scroll-margin} restricts how close point can come | 289 | The variable @code{scroll-margin} restricts how close point can come |
| 289 | to the top or bottom of a window (even if aggressive scrolling | 290 | to the top or bottom of a window (even if aggressive scrolling |
| 290 | specifies a fraction @var{f} that is larger than the window portion | 291 | specifies a fraction @var{f} that is larger than the window portion |
| 291 | between the top and the bottom margins). Its value is a number of screen | 292 | between the top and the bottom margins). Its value is a number of |
| 292 | lines; if point comes within that many lines of the top or bottom of | 293 | screen lines; if point comes within that many lines of the top or |
| 293 | the window, Emacs performs automatic scrolling. By default, | 294 | bottom of the window, Emacs performs automatic scrolling. By default, |
| 294 | @code{scroll-margin} is 0. | 295 | @code{scroll-margin} is 0. The effective margin size is limited to a |
| 296 | quarter of the window height by default, but this limit can be | ||
| 297 | increased up to half (or decreased down to zero) by customizing | ||
| 298 | @code{maximum-scroll-margin}. | ||
| 295 | 299 | ||
| 296 | @node Horizontal Scrolling | 300 | @node Horizontal Scrolling |
| 297 | @section Horizontal Scrolling | 301 | @section Horizontal Scrolling |
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 6f3de0c8a0e..affa28c9202 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi | |||
| @@ -3924,6 +3924,21 @@ redisplay scrolls the text automatically (if possible) to move point | |||
| 3924 | out of the margin, closer to the center of the window. | 3924 | out of the margin, closer to the center of the window. |
| 3925 | @end defopt | 3925 | @end defopt |
| 3926 | 3926 | ||
| 3927 | @defopt maximum-scroll-margin | ||
| 3928 | This variable limits the effective value of @code{scroll-margin} to a | ||
| 3929 | fraction of the current window line height. For example, if the | ||
| 3930 | current window has 20 lines and @code{maximum-scroll-margin} is 0.1, | ||
| 3931 | then the scroll margins will never be larger than 2 lines, no matter | ||
| 3932 | how big @code{scroll-margin} is. | ||
| 3933 | |||
| 3934 | @code{maximum-scroll-margin} itself has a maximum value of 0.5, which | ||
| 3935 | allows setting margins large to keep the cursor at the middle line of | ||
| 3936 | the window (or two middle lines if the window has an even number of | ||
| 3937 | lines). If it's set to a larger value (or any value other than a | ||
| 3938 | float between 0.0 and 0.5) then the default value of 0.25 will be used | ||
| 3939 | instead. | ||
| 3940 | @end defopt | ||
| 3941 | |||
| 3927 | @defopt scroll-conservatively | 3942 | @defopt scroll-conservatively |
| 3928 | This variable controls how scrolling is done automatically when point | 3943 | This variable controls how scrolling is done automatically when point |
| 3929 | moves off the screen (or into the scroll margin). If the value is a | 3944 | moves off the screen (or into the scroll margin). If the value is a |
| @@ -307,6 +307,11 @@ local part of a remote file name. Thus, if you have a directory named | |||
| 307 | "/~" on the remote host "foo", you can prevent it from being | 307 | "/~" on the remote host "foo", you can prevent it from being |
| 308 | substituted by a home directory by writing it as "/foo:/:/~/file". | 308 | substituted by a home directory by writing it as "/foo:/:/~/file". |
| 309 | 309 | ||
| 310 | +++ | ||
| 311 | ** The new variable 'maximum-scroll-margin' allows having effective | ||
| 312 | settings of 'scroll-margin' up to half the window size, instead of | ||
| 313 | always restricting the margin to a quarter of the window. | ||
| 314 | |||
| 310 | 315 | ||
| 311 | * Editing Changes in Emacs 26.1 | 316 | * Editing Changes in Emacs 26.1 |
| 312 | 317 | ||
diff --git a/lisp/cus-start.el b/lisp/cus-start.el index a790419b86f..51c43c7d21a 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el | |||
| @@ -511,6 +511,7 @@ since it could result in memory overflow and make Emacs crash." | |||
| 511 | (scroll-step windows integer) | 511 | (scroll-step windows integer) |
| 512 | (scroll-conservatively windows integer) | 512 | (scroll-conservatively windows integer) |
| 513 | (scroll-margin windows integer) | 513 | (scroll-margin windows integer) |
| 514 | (maximum-scroll-margin windows float "26.1") | ||
| 514 | (hscroll-margin windows integer "22.1") | 515 | (hscroll-margin windows integer "22.1") |
| 515 | (hscroll-step windows number "22.1") | 516 | (hscroll-step windows number "22.1") |
| 516 | (truncate-partial-width-windows | 517 | (truncate-partial-width-windows |
diff --git a/src/dispextern.h b/src/dispextern.h index 51222e636be..eb71a82311c 100644 --- a/src/dispextern.h +++ b/src/dispextern.h | |||
| @@ -3263,6 +3263,7 @@ void move_it_past_eol (struct it *); | |||
| 3263 | void move_it_in_display_line (struct it *it, | 3263 | void move_it_in_display_line (struct it *it, |
| 3264 | ptrdiff_t to_charpos, int to_x, | 3264 | ptrdiff_t to_charpos, int to_x, |
| 3265 | enum move_operation_enum op); | 3265 | enum move_operation_enum op); |
| 3266 | int partial_line_height (struct it *it_origin); | ||
| 3266 | bool in_display_vector_p (struct it *); | 3267 | bool in_display_vector_p (struct it *); |
| 3267 | int frame_mode_line_height (struct frame *); | 3268 | int frame_mode_line_height (struct frame *); |
| 3268 | extern bool redisplaying_p; | 3269 | extern bool redisplaying_p; |
diff --git a/src/window.c b/src/window.c index bc3f488f37f..95690443f8e 100644 --- a/src/window.c +++ b/src/window.c | |||
| @@ -4790,6 +4790,36 @@ window_scroll (Lisp_Object window, EMACS_INT n, bool whole, bool noerror) | |||
| 4790 | XWINDOW (window)->window_end_valid = false; | 4790 | XWINDOW (window)->window_end_valid = false; |
| 4791 | } | 4791 | } |
| 4792 | 4792 | ||
| 4793 | /* Compute scroll margin for WINDOW. | ||
| 4794 | We scroll when point is within this distance from the top or bottom | ||
| 4795 | of the window. The result is measured in lines or in pixels | ||
| 4796 | depending on the second parameter. */ | ||
| 4797 | int | ||
| 4798 | window_scroll_margin (struct window *window, enum margin_unit unit) | ||
| 4799 | { | ||
| 4800 | if (scroll_margin > 0) | ||
| 4801 | { | ||
| 4802 | int frame_line_height = default_line_pixel_height (window); | ||
| 4803 | int window_lines = window_box_height (window) / frame_line_height; | ||
| 4804 | |||
| 4805 | double ratio = 0.25; | ||
| 4806 | if (FLOATP (Vmaximum_scroll_margin)) | ||
| 4807 | { | ||
| 4808 | ratio = XFLOAT_DATA (Vmaximum_scroll_margin); | ||
| 4809 | ratio = max (0.0, ratio); | ||
| 4810 | ratio = min (ratio, 0.5); | ||
| 4811 | } | ||
| 4812 | int max_margin = min ((window_lines - 1)/2, | ||
| 4813 | (int) (window_lines * ratio)); | ||
| 4814 | int margin = clip_to_bounds (0, scroll_margin, max_margin); | ||
| 4815 | return (unit == MARGIN_IN_PIXELS) | ||
| 4816 | ? margin * frame_line_height | ||
| 4817 | : margin; | ||
| 4818 | } | ||
| 4819 | else | ||
| 4820 | return 0; | ||
| 4821 | } | ||
| 4822 | |||
| 4793 | 4823 | ||
| 4794 | /* Implementation of window_scroll that works based on pixel line | 4824 | /* Implementation of window_scroll that works based on pixel line |
| 4795 | heights. See the comment of window_scroll for parameter | 4825 | heights. See the comment of window_scroll for parameter |
| @@ -4806,7 +4836,6 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) | |||
| 4806 | bool vscrolled = false; | 4836 | bool vscrolled = false; |
| 4807 | int x, y, rtop, rbot, rowh, vpos; | 4837 | int x, y, rtop, rbot, rowh, vpos; |
| 4808 | void *itdata = NULL; | 4838 | void *itdata = NULL; |
| 4809 | int window_total_lines; | ||
| 4810 | int frame_line_height = default_line_pixel_height (w); | 4839 | int frame_line_height = default_line_pixel_height (w); |
| 4811 | bool adjust_old_pointm = !NILP (Fequal (Fwindow_point (window), | 4840 | bool adjust_old_pointm = !NILP (Fequal (Fwindow_point (window), |
| 4812 | Fwindow_old_point (window))); | 4841 | Fwindow_old_point (window))); |
| @@ -5062,12 +5091,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) | |||
| 5062 | /* Move PT out of scroll margins. | 5091 | /* Move PT out of scroll margins. |
| 5063 | This code wants current_y to be zero at the window start position | 5092 | This code wants current_y to be zero at the window start position |
| 5064 | even if there is a header line. */ | 5093 | even if there is a header line. */ |
| 5065 | window_total_lines | 5094 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); |
| 5066 | = w->total_lines * WINDOW_FRAME_LINE_HEIGHT (w) / frame_line_height; | ||
| 5067 | this_scroll_margin = max (0, scroll_margin); | ||
| 5068 | this_scroll_margin | ||
| 5069 | = min (this_scroll_margin, window_total_lines / 4); | ||
| 5070 | this_scroll_margin *= frame_line_height; | ||
| 5071 | 5095 | ||
| 5072 | if (n > 0) | 5096 | if (n > 0) |
| 5073 | { | 5097 | { |
| @@ -5123,7 +5147,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) | |||
| 5123 | in the scroll margin at the bottom. */ | 5147 | in the scroll margin at the bottom. */ |
| 5124 | move_it_to (&it, PT, -1, | 5148 | move_it_to (&it, PT, -1, |
| 5125 | (it.last_visible_y - WINDOW_HEADER_LINE_HEIGHT (w) | 5149 | (it.last_visible_y - WINDOW_HEADER_LINE_HEIGHT (w) |
| 5126 | - this_scroll_margin - 1), | 5150 | - partial_line_height (&it) - this_scroll_margin - 1), |
| 5127 | -1, | 5151 | -1, |
| 5128 | MOVE_TO_POS | MOVE_TO_Y); | 5152 | MOVE_TO_POS | MOVE_TO_Y); |
| 5129 | 5153 | ||
| @@ -5290,9 +5314,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) | |||
| 5290 | 5314 | ||
| 5291 | if (pos < ZV) | 5315 | if (pos < ZV) |
| 5292 | { | 5316 | { |
| 5293 | /* Don't use a scroll margin that is negative or too large. */ | 5317 | int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); |
| 5294 | int this_scroll_margin = | ||
| 5295 | max (0, min (scroll_margin, w->total_lines / 4)); | ||
| 5296 | 5318 | ||
| 5297 | set_marker_restricted_both (w->start, w->contents, pos, pos_byte); | 5319 | set_marker_restricted_both (w->start, w->contents, pos, pos_byte); |
| 5298 | w->start_at_line_beg = !NILP (bolp); | 5320 | w->start_at_line_beg = !NILP (bolp); |
| @@ -5722,8 +5744,7 @@ and redisplay normally--don't erase and redraw the frame. */) | |||
| 5722 | 5744 | ||
| 5723 | /* Do this after making BUF current | 5745 | /* Do this after making BUF current |
| 5724 | in case scroll_margin is buffer-local. */ | 5746 | in case scroll_margin is buffer-local. */ |
| 5725 | this_scroll_margin | 5747 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); |
| 5726 | = max (0, min (scroll_margin, w->total_lines / 4)); | ||
| 5727 | 5748 | ||
| 5728 | /* Don't use redisplay code for initial frames, as the necessary | 5749 | /* Don't use redisplay code for initial frames, as the necessary |
| 5729 | data structures might not be set up yet then. */ | 5750 | data structures might not be set up yet then. */ |
| @@ -5962,10 +5983,6 @@ from the top of the window. */) | |||
| 5962 | 5983 | ||
| 5963 | lines = displayed_window_lines (w); | 5984 | lines = displayed_window_lines (w); |
| 5964 | 5985 | ||
| 5965 | #if false | ||
| 5966 | this_scroll_margin = max (0, min (scroll_margin, lines / 4)); | ||
| 5967 | #endif | ||
| 5968 | |||
| 5969 | if (NILP (arg)) | 5986 | if (NILP (arg)) |
| 5970 | XSETFASTINT (arg, lines / 2); | 5987 | XSETFASTINT (arg, lines / 2); |
| 5971 | else | 5988 | else |
| @@ -5981,6 +5998,8 @@ from the top of the window. */) | |||
| 5981 | it is probably better not to install it. However, it is here | 5998 | it is probably better not to install it. However, it is here |
| 5982 | inside #if false so as not to lose it. -- rms. */ | 5999 | inside #if false so as not to lose it. -- rms. */ |
| 5983 | 6000 | ||
| 6001 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES); | ||
| 6002 | |||
| 5984 | /* Don't let it get into the margin at either top or bottom. */ | 6003 | /* Don't let it get into the margin at either top or bottom. */ |
| 5985 | iarg = max (iarg, this_scroll_margin); | 6004 | iarg = max (iarg, this_scroll_margin); |
| 5986 | iarg = min (iarg, lines - this_scroll_margin - 1); | 6005 | iarg = min (iarg, lines - this_scroll_margin - 1); |
diff --git a/src/window.h b/src/window.h index 061cf244943..acb8a5cabfa 100644 --- a/src/window.h +++ b/src/window.h | |||
| @@ -1120,6 +1120,8 @@ extern bool compare_window_configurations (Lisp_Object, Lisp_Object, bool); | |||
| 1120 | extern void mark_window_cursors_off (struct window *); | 1120 | extern void mark_window_cursors_off (struct window *); |
| 1121 | extern int window_internal_height (struct window *); | 1121 | extern int window_internal_height (struct window *); |
| 1122 | extern int window_body_width (struct window *w, bool); | 1122 | extern int window_body_width (struct window *w, bool); |
| 1123 | enum margin_unit { MARGIN_IN_LINES, MARGIN_IN_PIXELS }; | ||
| 1124 | extern int window_scroll_margin (struct window *, enum margin_unit); | ||
| 1123 | extern void temp_output_buffer_show (Lisp_Object); | 1125 | extern void temp_output_buffer_show (Lisp_Object); |
| 1124 | extern void replace_buffer_in_windows (Lisp_Object); | 1126 | extern void replace_buffer_in_windows (Lisp_Object); |
| 1125 | extern void replace_buffer_in_windows_safely (Lisp_Object); | 1127 | extern void replace_buffer_in_windows_safely (Lisp_Object); |
diff --git a/src/xdisp.c b/src/xdisp.c index 33661c882cd..0e329dfe6e9 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -9859,6 +9859,32 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos) | |||
| 9859 | } | 9859 | } |
| 9860 | } | 9860 | } |
| 9861 | 9861 | ||
| 9862 | int | ||
| 9863 | partial_line_height (struct it *it_origin) | ||
| 9864 | { | ||
| 9865 | int partial_height; | ||
| 9866 | void *it_data = NULL; | ||
| 9867 | struct it it; | ||
| 9868 | SAVE_IT (it, *it_origin, it_data); | ||
| 9869 | move_it_to (&it, ZV, -1, it.last_visible_y, -1, | ||
| 9870 | MOVE_TO_POS | MOVE_TO_Y); | ||
| 9871 | if (it.what == IT_EOB) | ||
| 9872 | { | ||
| 9873 | int vis_height = it.last_visible_y - it.current_y; | ||
| 9874 | int height = it.ascent + it.descent; | ||
| 9875 | partial_height = (vis_height < height) ? vis_height : 0; | ||
| 9876 | } | ||
| 9877 | else | ||
| 9878 | { | ||
| 9879 | int last_line_y = it.current_y; | ||
| 9880 | move_it_by_lines (&it, 1); | ||
| 9881 | partial_height = (it.current_y > it.last_visible_y) | ||
| 9882 | ? it.last_visible_y - last_line_y : 0; | ||
| 9883 | } | ||
| 9884 | RESTORE_IT (&it, &it, it_data); | ||
| 9885 | return partial_height; | ||
| 9886 | } | ||
| 9887 | |||
| 9862 | /* Return true if IT points into the middle of a display vector. */ | 9888 | /* Return true if IT points into the middle of a display vector. */ |
| 9863 | 9889 | ||
| 9864 | bool | 9890 | bool |
| @@ -15316,7 +15342,6 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, | |||
| 15316 | bool temp_scroll_step, bool last_line_misfit) | 15342 | bool temp_scroll_step, bool last_line_misfit) |
| 15317 | { | 15343 | { |
| 15318 | struct window *w = XWINDOW (window); | 15344 | struct window *w = XWINDOW (window); |
| 15319 | struct frame *f = XFRAME (w->frame); | ||
| 15320 | struct text_pos pos, startp; | 15345 | struct text_pos pos, startp; |
| 15321 | struct it it; | 15346 | struct it it; |
| 15322 | int this_scroll_margin, scroll_max, rc, height; | 15347 | int this_scroll_margin, scroll_max, rc, height; |
| @@ -15327,8 +15352,6 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, | |||
| 15327 | /* We will never try scrolling more than this number of lines. */ | 15352 | /* We will never try scrolling more than this number of lines. */ |
| 15328 | int scroll_limit = SCROLL_LIMIT; | 15353 | int scroll_limit = SCROLL_LIMIT; |
| 15329 | int frame_line_height = default_line_pixel_height (w); | 15354 | int frame_line_height = default_line_pixel_height (w); |
| 15330 | int window_total_lines | ||
| 15331 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | ||
| 15332 | 15355 | ||
| 15333 | #ifdef GLYPH_DEBUG | 15356 | #ifdef GLYPH_DEBUG |
| 15334 | debug_method_add (w, "try_scrolling"); | 15357 | debug_method_add (w, "try_scrolling"); |
| @@ -15336,13 +15359,7 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, | |||
| 15336 | 15359 | ||
| 15337 | SET_TEXT_POS_FROM_MARKER (startp, w->start); | 15360 | SET_TEXT_POS_FROM_MARKER (startp, w->start); |
| 15338 | 15361 | ||
| 15339 | /* Compute scroll margin height in pixels. We scroll when point is | 15362 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); |
| 15340 | within this distance from the top or bottom of the window. */ | ||
| 15341 | if (scroll_margin > 0) | ||
| 15342 | this_scroll_margin = min (scroll_margin, window_total_lines / 4) | ||
| 15343 | * frame_line_height; | ||
| 15344 | else | ||
| 15345 | this_scroll_margin = 0; | ||
| 15346 | 15363 | ||
| 15347 | /* Force arg_scroll_conservatively to have a reasonable value, to | 15364 | /* Force arg_scroll_conservatively to have a reasonable value, to |
| 15348 | avoid scrolling too far away with slow move_it_* functions. Note | 15365 | avoid scrolling too far away with slow move_it_* functions. Note |
| @@ -15377,7 +15394,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, | |||
| 15377 | /* Compute the pixel ypos of the scroll margin, then move IT to | 15394 | /* Compute the pixel ypos of the scroll margin, then move IT to |
| 15378 | either that ypos or PT, whichever comes first. */ | 15395 | either that ypos or PT, whichever comes first. */ |
| 15379 | start_display (&it, w, startp); | 15396 | start_display (&it, w, startp); |
| 15380 | scroll_margin_y = it.last_visible_y - this_scroll_margin | 15397 | scroll_margin_y = it.last_visible_y - partial_line_height (&it) |
| 15398 | - this_scroll_margin | ||
| 15381 | - frame_line_height * extra_scroll_margin_lines; | 15399 | - frame_line_height * extra_scroll_margin_lines; |
| 15382 | move_it_to (&it, PT, -1, scroll_margin_y - 1, -1, | 15400 | move_it_to (&it, PT, -1, scroll_margin_y - 1, -1, |
| 15383 | (MOVE_TO_POS | MOVE_TO_Y)); | 15401 | (MOVE_TO_POS | MOVE_TO_Y)); |
| @@ -15816,23 +15834,12 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, | |||
| 15816 | { | 15834 | { |
| 15817 | int this_scroll_margin, top_scroll_margin; | 15835 | int this_scroll_margin, top_scroll_margin; |
| 15818 | struct glyph_row *row = NULL; | 15836 | struct glyph_row *row = NULL; |
| 15819 | int frame_line_height = default_line_pixel_height (w); | ||
| 15820 | int window_total_lines | ||
| 15821 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | ||
| 15822 | 15837 | ||
| 15823 | #ifdef GLYPH_DEBUG | 15838 | #ifdef GLYPH_DEBUG |
| 15824 | debug_method_add (w, "cursor movement"); | 15839 | debug_method_add (w, "cursor movement"); |
| 15825 | #endif | 15840 | #endif |
| 15826 | 15841 | ||
| 15827 | /* Scroll if point within this distance from the top or bottom | 15842 | this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); |
| 15828 | of the window. This is a pixel value. */ | ||
| 15829 | if (scroll_margin > 0) | ||
| 15830 | { | ||
| 15831 | this_scroll_margin = min (scroll_margin, window_total_lines / 4); | ||
| 15832 | this_scroll_margin *= frame_line_height; | ||
| 15833 | } | ||
| 15834 | else | ||
| 15835 | this_scroll_margin = 0; | ||
| 15836 | 15843 | ||
| 15837 | top_scroll_margin = this_scroll_margin; | 15844 | top_scroll_margin = this_scroll_margin; |
| 15838 | if (WINDOW_WANTS_HEADER_LINE_P (w)) | 15845 | if (WINDOW_WANTS_HEADER_LINE_P (w)) |
| @@ -16280,7 +16287,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 16280 | int centering_position = -1; | 16287 | int centering_position = -1; |
| 16281 | bool last_line_misfit = false; | 16288 | bool last_line_misfit = false; |
| 16282 | ptrdiff_t beg_unchanged, end_unchanged; | 16289 | ptrdiff_t beg_unchanged, end_unchanged; |
| 16283 | int frame_line_height; | 16290 | int frame_line_height, margin; |
| 16284 | bool use_desired_matrix; | 16291 | bool use_desired_matrix; |
| 16285 | void *itdata = NULL; | 16292 | void *itdata = NULL; |
| 16286 | 16293 | ||
| @@ -16310,6 +16317,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 16310 | restart: | 16317 | restart: |
| 16311 | reconsider_clip_changes (w); | 16318 | reconsider_clip_changes (w); |
| 16312 | frame_line_height = default_line_pixel_height (w); | 16319 | frame_line_height = default_line_pixel_height (w); |
| 16320 | margin = window_scroll_margin (w, MARGIN_IN_LINES); | ||
| 16321 | |||
| 16313 | 16322 | ||
| 16314 | /* Has the mode line to be updated? */ | 16323 | /* Has the mode line to be updated? */ |
| 16315 | update_mode_line = (w->update_mode_line | 16324 | update_mode_line = (w->update_mode_line |
| @@ -16614,10 +16623,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 16614 | /* Some people insist on not letting point enter the scroll | 16623 | /* Some people insist on not letting point enter the scroll |
| 16615 | margin, even though this part handles windows that didn't | 16624 | margin, even though this part handles windows that didn't |
| 16616 | scroll at all. */ | 16625 | scroll at all. */ |
| 16617 | int window_total_lines | 16626 | int pixel_margin = margin * frame_line_height; |
| 16618 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | ||
| 16619 | int margin = min (scroll_margin, window_total_lines / 4); | ||
| 16620 | int pixel_margin = margin * frame_line_height; | ||
| 16621 | bool header_line = WINDOW_WANTS_HEADER_LINE_P (w); | 16627 | bool header_line = WINDOW_WANTS_HEADER_LINE_P (w); |
| 16622 | 16628 | ||
| 16623 | /* Note: We add an extra FRAME_LINE_HEIGHT, because the loop | 16629 | /* Note: We add an extra FRAME_LINE_HEIGHT, because the loop |
| @@ -16901,12 +16907,6 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 16901 | it.current_y = it.last_visible_y; | 16907 | it.current_y = it.last_visible_y; |
| 16902 | if (centering_position < 0) | 16908 | if (centering_position < 0) |
| 16903 | { | 16909 | { |
| 16904 | int window_total_lines | ||
| 16905 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | ||
| 16906 | int margin | ||
| 16907 | = scroll_margin > 0 | ||
| 16908 | ? min (scroll_margin, window_total_lines / 4) | ||
| 16909 | : 0; | ||
| 16910 | ptrdiff_t margin_pos = CHARPOS (startp); | 16910 | ptrdiff_t margin_pos = CHARPOS (startp); |
| 16911 | Lisp_Object aggressive; | 16911 | Lisp_Object aggressive; |
| 16912 | bool scrolling_up; | 16912 | bool scrolling_up; |
| @@ -17150,10 +17150,6 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) | |||
| 17150 | { | 17150 | { |
| 17151 | int window_total_lines | 17151 | int window_total_lines |
| 17152 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | 17152 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; |
| 17153 | int margin = | ||
| 17154 | scroll_margin > 0 | ||
| 17155 | ? min (scroll_margin, window_total_lines / 4) | ||
| 17156 | : 0; | ||
| 17157 | bool move_down = w->cursor.vpos >= window_total_lines / 2; | 17153 | bool move_down = w->cursor.vpos >= window_total_lines / 2; |
| 17158 | 17154 | ||
| 17159 | move_it_by_lines (&it, move_down ? margin + 1 : -(margin + 1)); | 17155 | move_it_by_lines (&it, move_down ? margin + 1 : -(margin + 1)); |
| @@ -17359,7 +17355,6 @@ try_window (Lisp_Object window, struct text_pos pos, int flags) | |||
| 17359 | struct it it; | 17355 | struct it it; |
| 17360 | struct glyph_row *last_text_row = NULL; | 17356 | struct glyph_row *last_text_row = NULL; |
| 17361 | struct frame *f = XFRAME (w->frame); | 17357 | struct frame *f = XFRAME (w->frame); |
| 17362 | int frame_line_height = default_line_pixel_height (w); | ||
| 17363 | 17358 | ||
| 17364 | /* Make POS the new window start. */ | 17359 | /* Make POS the new window start. */ |
| 17365 | set_marker_both (w->start, Qnil, CHARPOS (pos), BYTEPOS (pos)); | 17360 | set_marker_both (w->start, Qnil, CHARPOS (pos), BYTEPOS (pos)); |
| @@ -17385,17 +17380,7 @@ try_window (Lisp_Object window, struct text_pos pos, int flags) | |||
| 17385 | if ((flags & TRY_WINDOW_CHECK_MARGINS) | 17380 | if ((flags & TRY_WINDOW_CHECK_MARGINS) |
| 17386 | && !MINI_WINDOW_P (w)) | 17381 | && !MINI_WINDOW_P (w)) |
| 17387 | { | 17382 | { |
| 17388 | int this_scroll_margin; | 17383 | int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); |
| 17389 | int window_total_lines | ||
| 17390 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (f) / frame_line_height; | ||
| 17391 | |||
| 17392 | if (scroll_margin > 0) | ||
| 17393 | { | ||
| 17394 | this_scroll_margin = min (scroll_margin, window_total_lines / 4); | ||
| 17395 | this_scroll_margin *= frame_line_height; | ||
| 17396 | } | ||
| 17397 | else | ||
| 17398 | this_scroll_margin = 0; | ||
| 17399 | 17384 | ||
| 17400 | if ((w->cursor.y >= 0 /* not vscrolled */ | 17385 | if ((w->cursor.y >= 0 /* not vscrolled */ |
| 17401 | && w->cursor.y < this_scroll_margin | 17386 | && w->cursor.y < this_scroll_margin |
| @@ -18679,15 +18664,8 @@ try_window_id (struct window *w) | |||
| 18679 | 18664 | ||
| 18680 | /* Don't let the cursor end in the scroll margins. */ | 18665 | /* Don't let the cursor end in the scroll margins. */ |
| 18681 | { | 18666 | { |
| 18682 | int this_scroll_margin, cursor_height; | 18667 | int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); |
| 18683 | int frame_line_height = default_line_pixel_height (w); | 18668 | int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height; |
| 18684 | int window_total_lines | ||
| 18685 | = WINDOW_TOTAL_LINES (w) * FRAME_LINE_HEIGHT (it.f) / frame_line_height; | ||
| 18686 | |||
| 18687 | this_scroll_margin = | ||
| 18688 | max (0, min (scroll_margin, window_total_lines / 4)); | ||
| 18689 | this_scroll_margin *= frame_line_height; | ||
| 18690 | cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height; | ||
| 18691 | 18669 | ||
| 18692 | if ((w->cursor.y < this_scroll_margin | 18670 | if ((w->cursor.y < this_scroll_margin |
| 18693 | && CHARPOS (start) > BEGV) | 18671 | && CHARPOS (start) > BEGV) |
| @@ -31569,6 +31547,14 @@ Recenter the window whenever point gets within this many lines | |||
| 31569 | of the top or bottom of the window. */); | 31547 | of the top or bottom of the window. */); |
| 31570 | scroll_margin = 0; | 31548 | scroll_margin = 0; |
| 31571 | 31549 | ||
| 31550 | DEFVAR_LISP ("maximum-scroll-margin", Vmaximum_scroll_margin, | ||
| 31551 | doc: /* Maximum effective value of `scroll-margin'. | ||
| 31552 | Given as a fraction of the current window's lines. The value should | ||
| 31553 | be a floating point number between 0.0 and 0.5. The effective maximum | ||
| 31554 | is limited to (/ (1- window-lines) 2). Non-float values for this | ||
| 31555 | variable are ignored and the default 0.25 is used instead. */); | ||
| 31556 | Vmaximum_scroll_margin = make_float (0.25); | ||
| 31557 | |||
| 31572 | DEFVAR_LISP ("display-pixels-per-inch", Vdisplay_pixels_per_inch, | 31558 | DEFVAR_LISP ("display-pixels-per-inch", Vdisplay_pixels_per_inch, |
| 31573 | doc: /* Pixels per inch value for non-window system displays. | 31559 | doc: /* Pixels per inch value for non-window system displays. |
| 31574 | Value is a number or a cons (WIDTH-DPI . HEIGHT-DPI). */); | 31560 | Value is a number or a cons (WIDTH-DPI . HEIGHT-DPI). */); |
diff --git a/test/manual/scroll-tests.el b/test/manual/scroll-tests.el new file mode 100644 index 00000000000..1167efd6a66 --- /dev/null +++ b/test/manual/scroll-tests.el | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | ;;; scroll-tests.el -- tests for scrolling -*- lexical-binding: t -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | ;; This program is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 10 | ;; (at your option) any later version. | ||
| 11 | |||
| 12 | ;; This program is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | ;;; Commentary: | ||
| 21 | |||
| 22 | ;; These are mostly automated ert tests, but they don't work in batch | ||
| 23 | ;; mode which is why they are under test/manual. | ||
| 24 | |||
| 25 | ;;; Code: | ||
| 26 | |||
| 27 | (require 'ert) | ||
| 28 | (eval-when-compile (require 'cl-lib)) | ||
| 29 | |||
| 30 | (defun scroll-tests-up-and-down (margin &optional effective-margin) | ||
| 31 | (unless effective-margin | ||
| 32 | (setq effective-margin margin)) | ||
| 33 | (erase-buffer) | ||
| 34 | (insert (mapconcat #'number-to-string | ||
| 35 | (number-sequence 1 200) "\n")) | ||
| 36 | (goto-char 1) | ||
| 37 | (sit-for 0) | ||
| 38 | (let ((scroll-margin margin) | ||
| 39 | (wstart (window-start))) | ||
| 40 | ;; Stopping before `scroll-margin' so we shouldn't have | ||
| 41 | ;; scrolled. | ||
| 42 | (let ((current-prefix-arg (- (window-text-height) 1 effective-margin))) | ||
| 43 | (call-interactively 'next-line)) | ||
| 44 | (sit-for 0) | ||
| 45 | (should (= wstart (window-start))) | ||
| 46 | ;; Passing `scroll-margin' should trigger scrolling. | ||
| 47 | (call-interactively 'next-line) | ||
| 48 | (sit-for 0) | ||
| 49 | (should (/= wstart (window-start))) | ||
| 50 | ;; Scroll back to top. | ||
| 51 | (let ((current-prefix-arg (window-start))) | ||
| 52 | (call-interactively 'scroll-down-command)) | ||
| 53 | (sit-for 0) | ||
| 54 | (should (= 1 (window-start))))) | ||
| 55 | |||
| 56 | (defmacro scroll-tests-with-buffer-window (&rest body) | ||
| 57 | (declare (debug t)) | ||
| 58 | `(with-temp-buffer | ||
| 59 | (with-selected-window (display-buffer (current-buffer)) | ||
| 60 | ,@body))) | ||
| 61 | |||
| 62 | (ert-deftest scroll-tests-scroll-margin-0 () | ||
| 63 | (skip-unless (not noninteractive)) | ||
| 64 | (scroll-tests-with-buffer-window | ||
| 65 | (scroll-tests-up-and-down 0))) | ||
| 66 | |||
| 67 | (ert-deftest scroll-tests-scroll-margin-negative () | ||
| 68 | "A negative `scroll-margin' should be the same as 0." | ||
| 69 | (skip-unless (not noninteractive)) | ||
| 70 | (scroll-tests-with-buffer-window | ||
| 71 | (scroll-tests-up-and-down -10 0))) | ||
| 72 | |||
| 73 | (ert-deftest scroll-tests-scroll-margin-max () | ||
| 74 | (skip-unless (not noninteractive)) | ||
| 75 | (scroll-tests-with-buffer-window | ||
| 76 | (let ((max-margin (/ (window-text-height) 4))) | ||
| 77 | (scroll-tests-up-and-down max-margin)))) | ||
| 78 | |||
| 79 | (ert-deftest scroll-tests-scroll-margin-over-max () | ||
| 80 | "A `scroll-margin' more than max should be the same as max." | ||
| 81 | (skip-unless (not noninteractive)) | ||
| 82 | (scroll-tests-with-buffer-window | ||
| 83 | (set-window-text-height nil 7) | ||
| 84 | (let ((max-margin (/ (window-text-height) 4))) | ||
| 85 | (scroll-tests-up-and-down (+ max-margin 1) max-margin) | ||
| 86 | (scroll-tests-up-and-down (+ max-margin 2) max-margin)))) | ||
| 87 | |||
| 88 | (defun scroll-tests--point-in-middle-of-window-p () | ||
| 89 | (= (count-lines (window-start) (window-point)) | ||
| 90 | (/ (1- (window-text-height)) 2))) | ||
| 91 | |||
| 92 | (cl-defun scroll-tests--scroll-margin-whole-window (&key with-line-spacing) | ||
| 93 | "Test `maximum-scroll-margin' at 0.5. | ||
| 94 | With a high `scroll-margin', this should keep cursor in the | ||
| 95 | middle of the window." | ||
| 96 | (let ((maximum-scroll-margin 0.5) | ||
| 97 | (scroll-margin 100)) | ||
| 98 | (scroll-tests-with-buffer-window | ||
| 99 | (setq-local line-spacing with-line-spacing) | ||
| 100 | ;; Choose an odd number, so there is one line in the middle. | ||
| 101 | (set-window-text-height nil 7) | ||
| 102 | ;; `set-window-text-height' doesn't count `line-spacing'. | ||
| 103 | (when with-line-spacing | ||
| 104 | (window-resize nil (* line-spacing 7) nil nil 'pixels)) | ||
| 105 | (erase-buffer) | ||
| 106 | (insert (mapconcat #'number-to-string | ||
| 107 | (number-sequence 1 200) "\n")) | ||
| 108 | (goto-char 1) | ||
| 109 | (sit-for 0) | ||
| 110 | (call-interactively 'scroll-up-command) | ||
| 111 | (sit-for 0) | ||
| 112 | (should (scroll-tests--point-in-middle-of-window-p)) | ||
| 113 | (call-interactively 'scroll-up-command) | ||
| 114 | (sit-for 0) | ||
| 115 | (should (scroll-tests--point-in-middle-of-window-p)) | ||
| 116 | (call-interactively 'scroll-down-command) | ||
| 117 | (sit-for 0) | ||
| 118 | (should (scroll-tests--point-in-middle-of-window-p))))) | ||
| 119 | |||
| 120 | (ert-deftest scroll-tests-scroll-margin-whole-window () | ||
| 121 | (skip-unless (not noninteractive)) | ||
| 122 | (scroll-tests--scroll-margin-whole-window)) | ||
| 123 | |||
| 124 | (ert-deftest scroll-tests-scroll-margin-whole-window-line-spacing () | ||
| 125 | ;; `line-spacing' has no effect on tty displays. | ||
| 126 | (skip-unless (display-graphic-p)) | ||
| 127 | (scroll-tests--scroll-margin-whole-window :with-line-spacing 3)) | ||
| 128 | |||
| 129 | |||
| 130 | ;;; scroll-tests.el ends here | ||