changeset 17180:8581779aa16f v8.1.1589

patch 8.1.1589: popup window does not indicate scroll position commit https://github.com/vim/vim/commit/75fb0854e93913c4d2cfcd6ef634173c4d13a093 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Jun 25 05:15:58 2019 +0200 patch 8.1.1589: popup window does not indicate scroll position Problem: Popup window does not indicate scroll position. Solution: Add a scrollbar.
author Bram Moolenaar <Bram@vim.org>
date Tue, 25 Jun 2019 05:30:06 +0200
parents 9c7fd1b6445b
children 249a79cc1d4a
files runtime/doc/popup.txt src/popupwin.c src/structs.h src/testdir/dumps/Test_popupwin_firstline.dump src/testdir/dumps/Test_popupwin_scroll_1.dump src/testdir/dumps/Test_popupwin_scroll_2.dump src/testdir/dumps/Test_popupwin_scroll_3.dump src/testdir/dumps/Test_popupwin_scroll_4.dump src/testdir/test_popupwin.vim src/version.c
diffstat 10 files changed, 143 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -87,9 +87,10 @@ that it is in.
 
 
 TODO:
+- When the lines do not fit show a scrollbar (like in the popup menu).
+- Use the mouse wheel for scrolling.
+- Have a way to scroll to the botton. (#4577)
 - Why does 'nrformats' leak from the popup window buffer???
-- When the lines do not fit show a scrollbar (like in the popup menu).
-  Use the mouse wheel for scrolling.
 - Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
   Use ERROR_IF_POPUP_WINDOW for more commands.
 - Add 'balloonpopup': instead of showing text, let the callback open a popup
@@ -347,6 +348,9 @@ popup_notification({text}, {options})			
 <		The PopupNotification highlight group is used instead of
 		WarningMsg if it is defined.
 
+		This popup should only be used with the |+timers| feature,
+		otherwise it will not disappear.
+
 		The position will be adjusted to avoid overlap with other
 		notifications.
 		Use {options} to change the properties.
@@ -370,6 +374,7 @@ popup_setoptions({id}, {options})			*pop
 			border
 			borderhighlight
 			borderchars
+			scrollbar
 			zindex
 			mask
 			time
@@ -475,6 +480,7 @@ The second argument of |popup_create()| 
 	firstline	First buffer line to display.  When larger than one it
 			looks like the text scrolled up.  When out of range
 			the last buffer line will at the top of the window.
+			Also see "scrollbar".
 	hidden		When TRUE the popup exists but is not displayed; use
 			`popup_show()` to unhide it.
 			{not implemented yet}
@@ -525,6 +531,8 @@ The second argument of |popup_create()| 
 			By default a double line is used all around when
 			'encoding' is "utf-8" and 'ambiwidth' is "single",
 			otherwise ASCII characters are used.
+	scrollbar	non-zero: show a scrollbar when the text doesn't fit.
+			zero: do not show a scrollbar.  Default is non-zero.
 	zindex		Priority for the popup, default 50.  Minimum value is
 			1, maximum value is 32000.
 	mask		A list of lists with coordinates, defining parts of
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -287,6 +287,10 @@ apply_general_options(win_T *wp, dict_T 
     if (wp->w_firstline < 1)
 	wp->w_firstline = 1;
 
+    di = dict_find(dict, (char_u *)"scrollbar", -1);
+    if (di != NULL)
+	wp->w_want_scrollbar = dict_get_number(dict, (char_u *)"scrollbar");
+
     str = dict_get_string(dict, (char_u *)"title", FALSE);
     if (str != NULL)
     {
@@ -733,6 +737,9 @@ popup_adjust_position(win_T *wp)
 	    break;
     }
 
+    wp->w_has_scrollbar = wp->w_want_scrollbar
+	   && (wp->w_topline > 1 || lnum <= wp->w_buffer->b_ml.ml_line_count);
+
     minwidth = wp->w_minwidth;
     if (wp->w_popup_title != NULL && *wp->w_popup_title != NUL)
     {
@@ -1047,6 +1054,7 @@ popup_create(typval_T *argvars, typval_T
 	VIM_CLEAR(wp->w_border_highlight[i]);
     for (i = 0; i < 8; ++i)
 	wp->w_border_char[i] = 0;
+    wp->w_want_scrollbar = 1;
 
     // Deal with options.
     apply_options(wp, argvars[1].vval.v_dict);
@@ -1483,6 +1491,7 @@ f_popup_setoptions(typval_T *argvars, ty
     dict_T	*dict;
     int		id = (int)tv_get_number(argvars);
     win_T	*wp = find_popup_win(id);
+    linenr_T	old_firstline;
 
     if (wp == NULL)
 	return;  // invalid {id}
@@ -1493,10 +1502,13 @@ f_popup_setoptions(typval_T *argvars, ty
 	return;
     }
     dict = argvars[1].vval.v_dict;
+    old_firstline = wp->w_firstline;
 
     apply_move_options(wp, dict);
     apply_general_options(wp, dict);
 
+    if (old_firstline != wp->w_firstline)
+	redraw_win_later(wp, NOT_VALID);
     popup_mask_refresh = TRUE;
     popup_adjust_position(wp);
 }
@@ -1534,6 +1546,7 @@ f_popup_getpos(typval_T *argvars, typval
 	dict_add_number(dict, "core_width", wp->w_width);
 	dict_add_number(dict, "core_height", wp->w_height);
 
+	dict_add_number(dict, "scrollbar", wp->w_has_scrollbar);
 	dict_add_number(dict, "visible",
 		      win_valid(wp) && (wp->w_popup_flags & POPF_HIDDEN) == 0);
     }
@@ -1656,6 +1669,7 @@ f_popup_getoptions(typval_T *argvars, ty
 	dict_add_number(dict, "maxheight", wp->w_maxheight);
 	dict_add_number(dict, "maxwidth", wp->w_maxwidth);
 	dict_add_number(dict, "firstline", wp->w_firstline);
+	dict_add_number(dict, "scrollbar", wp->w_want_scrollbar);
 	dict_add_number(dict, "zindex", wp->w_zindex);
 	dict_add_number(dict, "fixed", wp->w_popup_fixed);
 	dict_add_string(dict, "title", wp->w_popup_title);
@@ -2114,6 +2128,10 @@ update_popups(void (*win_update)(win_T *
     char_u  buf[MB_MAXBYTES];
     int	    row;
     int	    i;
+    int	    sb_thumb_top;
+    int	    sb_thumb_height;
+    int	    attr_scroll = highlight_attr[HLF_PSB];
+    int	    attr_thumb = highlight_attr[HLF_PST];
 
     // Find the window with the lowest zindex that hasn't been updated yet,
     // so that the window with a higher zindex is drawn later, thus goes on
@@ -2143,7 +2161,8 @@ update_popups(void (*win_update)(win_T *
 	wp->w_wincol -= left_off;
 
 	total_width = wp->w_popup_border[3] + wp->w_popup_padding[3]
-		+ wp->w_width + wp->w_popup_padding[1] + wp->w_popup_border[1];
+		+ wp->w_width + wp->w_popup_padding[1] + wp->w_popup_border[1]
+		+ wp->w_has_scrollbar;
 	total_height = popup_top_extra(wp)
 		+ wp->w_height + wp->w_popup_padding[2] + wp->w_popup_border[2];
 	popup_attr = get_wcr_attr(wp);
@@ -2203,7 +2222,8 @@ update_popups(void (*win_update)(win_T *
 	    row = wp->w_winrow + wp->w_popup_border[0];
 	    screen_fill(row, row + top_padding,
 		    wp->w_wincol + wp->w_popup_border[3],
-		    wp->w_wincol + total_width - wp->w_popup_border[1],
+		    wp->w_wincol + total_width - wp->w_popup_border[1]
+							- wp->w_has_scrollbar,
 							 ' ', ' ', popup_attr);
 	}
 
@@ -2212,10 +2232,24 @@ update_popups(void (*win_update)(win_T *
 	    screen_puts(wp->w_popup_title, wp->w_winrow, wp->w_wincol + 1,
 		    wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
 
-	for (row = wp->w_winrow + wp->w_popup_border[0];
-		row < wp->w_winrow + total_height - wp->w_popup_border[2];
-		    ++row)
+	// Compute scrollbar thumb position and size.
+	if (wp->w_has_scrollbar)
 	{
+	    linenr_T linecount = wp->w_buffer->b_ml.ml_line_count;
+
+	    sb_thumb_height = wp->w_height * wp->w_height / linecount;
+	    if (sb_thumb_height == 0)
+		sb_thumb_height = 1;
+	    sb_thumb_top = ((wp->w_topline * (wp->w_height - sb_thumb_height)
+			    + (linecount - wp->w_height) / 2))
+			      / (linecount - (wp->w_height - sb_thumb_height));
+	}
+
+	for (i = wp->w_popup_border[0];
+				 i < total_height - wp->w_popup_border[2]; ++i)
+	{
+	    row = wp->w_winrow + i;
+
 	    // left border
 	    if (wp->w_popup_border[3] > 0)
 	    {
@@ -2226,6 +2260,21 @@ update_popups(void (*win_update)(win_T *
 	    if (wp->w_popup_padding[3] > 0)
 		screen_puts(get_spaces(wp->w_popup_padding[3]), row,
 			wp->w_wincol + wp->w_popup_border[3], popup_attr);
+	    // scrollbar
+	    if (wp->w_has_scrollbar)
+	    {
+		int line = i - top_off;
+		int scroll_col = wp->w_wincol + total_width - 1
+						       - wp->w_popup_border[1];
+
+		if (line >= 0 && line < wp->w_height)
+		    screen_putchar(' ', row, scroll_col,
+			    line >= sb_thumb_top
+				       && line < sb_thumb_top + sb_thumb_height
+						  ? attr_thumb : attr_scroll);
+		else
+		    screen_putchar(' ', row, scroll_col, popup_attr);
+	    }
 	    // right border
 	    if (wp->w_popup_border[1] > 0)
 	    {
--- a/src/structs.h
+++ b/src/structs.h
@@ -2902,6 +2902,8 @@ struct window_S
     int		w_wantline;	    // "line" for popup window
     int		w_wantcol;	    // "col" for popup window
     int		w_firstline;	    // "firstline" for popup window
+    int		w_want_scrollbar;   // when zero don't use a scrollbar
+    int		w_has_scrollbar;    // scrollbar displayed
     int		w_popup_padding[4]; // popup padding top/right/bot/left
     int		w_popup_border[4];  // popup border top/right/bot/left
     char_u	*w_border_highlight[4];  // popup border highlight
--- a/src/testdir/dumps/Test_popupwin_firstline.dump
+++ b/src/testdir/dumps/Test_popupwin_firstline.dump
@@ -1,10 +1,10 @@
 >1+0&#ffffff0| @73
 |2| @73
 |3| @73
-|4| @33|3+0#0000001#ffd7ff255@4| +0#0000000#ffffff0@34
-|5| @33|4+0#0000001#ffd7ff255@1| @2| +0#0000000#ffffff0@34
-|6| @33|5+0#0000001#ffd7ff255| @3| +0#0000000#ffffff0@34
-|7| @33|6+0#0000001#ffd7ff255@4| +0#0000000#ffffff0@34
+|4| @33|3+0#0000001#ffd7ff255@4| +0#0000000#a8a8a8255| +0&#ffffff0@33
+|5| @33|4+0#0000001#ffd7ff255@1| @2| +0#0000000#0000001| +0&#ffffff0@33
+|6| @33|5+0#0000001#ffd7ff255| @3| +0#0000000#a8a8a8255| +0&#ffffff0@33
+|7| @33|6+0#0000001#ffd7ff255@4| +0#0000000#a8a8a8255| +0&#ffffff0@33
 |8| @73
 |9| @73
 @57|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_scroll_1.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @31|o+0#0000001#ffd7ff255|n|e| @4| +0#0000000#0000001| +0&#ffffff0@32
+|5| @31|t+0#0000001#ffd7ff255|w|o| @4| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|6| @31|t+0#0000001#ffd7ff255|h|r|e@1| @2| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|7| @31|f+0#0000001#ffd7ff255|o|u|r| @3| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|8| @73
+|9| @73
+@57|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_scroll_2.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @31|t+0#0000001#ffd7ff255|w|o| @4| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|5| @31|t+0#0000001#ffd7ff255|h|r|e@1| @2| +0#0000000#0000001| +0&#ffffff0@32
+|6| @31|f+0#0000001#ffd7ff255|o|u|r| @3| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|7| @31|f+0#0000001#ffd7ff255|i|v|e| @3| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|8| @73
+|9| @73
+|:|c|a|l@1| |p|o|p|u|p|_|s|e|t|o|p|t|i|o|n|s|(|w|i|n|i|d|,| |{|'|f|i|r|s|t|l|i|n|e|'|:| |2|}|)| @9|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_scroll_3.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @31|s+0#0000001#ffd7ff255|i|x| @4| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|5| @31|s+0#0000001#ffd7ff255|e|v|e|n| @2| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|6| @31|e+0#0000001#ffd7ff255|i|g|h|t| @2| +0#0000000#a8a8a8255| +0&#ffffff0@32
+|7| @31|n+0#0000001#ffd7ff255|i|n|e| @3| +0#0000000#0000001| +0&#ffffff0@32
+|8| @73
+|9| @73
+|:|c|a|l@1| |p|o|p|u|p|_|s|e|t|o|p|t|i|o|n|s|(|w|i|n|i|d|,| |{|'|f|i|r|s|t|l|i|n|e|'|:| |6|}|)| @9|1|,|1| @10|T|o|p| 
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_scroll_4.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @73
+|5| @31|n+0#0000001#ffd7ff255|i|n|e| @3| +0#0000000#0000001| +0&#ffffff0@32
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+|:|c|a|l@1| |p|o|p|u|p|_|s|e|t|o|p|t|i|o|n|s|(|w|i|n|i|d|,| |{|'|f|i|r|s|t|l|i|n|e|'|:| |9|}|)| @9|1|,|1| @10|T|o|p| 
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -1407,10 +1407,40 @@ func Test_notifications()
   call term_sendkeys(buf, ":call popup_notification('another important notification', {})\<CR>")
   call VerifyScreenDump(buf, 'Test_popupwin_notify_02', {})
 
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('XtestNotifications')
+endfunc
+
+func Test_popup_scrollbar()
+  if !CanRunVimInTerminal()
+    throw 'Skipped: cannot make screendumps'
+  endif
+
+  let lines =<< trim END
+    call setline(1, range(1, 20))
+    let winid = popup_create(['one', 'two', 'three', 'four', 'five',
+	  \ 'six', 'seven', 'eight', 'nine'], {
+	  \ 'minwidth': 8,
+	  \ 'maxheight': 4,
+	  \ })
+  END
+  call writefile(lines, 'XtestPopupScroll')
+  let buf = RunVimInTerminal('-S XtestPopupScroll', {'rows': 10})
+  call VerifyScreenDump(buf, 'Test_popupwin_scroll_1', {})
+
+  call term_sendkeys(buf, ":call popup_setoptions(winid, {'firstline': 2})\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_scroll_2', {})
+
+  call term_sendkeys(buf, ":call popup_setoptions(winid, {'firstline': 6})\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_scroll_3', {})
+
+  call term_sendkeys(buf, ":call popup_setoptions(winid, {'firstline': 9})\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_scroll_4', {})
 
   " clean up
   call StopVimInTerminal(buf)
-  call delete('XtestNotifications')
+  call delete('XtestPopupScroll')
 endfunc
 
 func Test_popup_settext()
--- a/src/version.c
+++ b/src/version.c
@@ -778,6 +778,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1589,
+/**/
     1588,
 /**/
     1587,