changeset 16884:59e4148c0c73 v8.1.1443

patch 8.1.1443: popup window padding and border not implemented yet commit https://github.com/vim/vim/commit/2fd8e35e16e502c98045c4b4e09a91eca840fb97 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jun 1 20:16:48 2019 +0200 patch 8.1.1443: popup window padding and border not implemented yet Problem: Popup window padding and border not implemented yet. Solution: Implement padding and border. Add core position and size to popup_getpos().
author Bram Moolenaar <Bram@vim.org>
date Sat, 01 Jun 2019 20:30:05 +0200
parents c7c1b4618acb
children 4aed9232c8b7
files runtime/doc/popup.txt src/popupwin.c src/screen.c src/structs.h src/testdir/dumps/Test_popupwin_20.dump src/testdir/test_popupwin.vim src/version.c
diffstat 7 files changed, 254 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -90,11 +90,7 @@ Probably 2. is the best choice.
 
 IMPLEMENTATION:
 - Code is in popupwin.c
-- Invoke filter with character before mapping?
-- Handle screen resize in screenalloc(). (Ben Jackson, #4467)
 - Why does 'nrformats' leak from the popup window buffer???
-- Implement padding
-- Implement border
 - Make redrawing more efficient and avoid flicker.
     Store popup info in a mask, use the mask in screen_line()
     Keep mask until next update_screen(), find differences and redraw affected
@@ -103,7 +99,8 @@ IMPLEMENTATION:
     Fix redrawing problem when scrolling non-current window
     Fix redrawing the statusline on top of a popup
 - Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
-  Use NOT_IN_POPUP_WINDOW.
+  Use NOT_IN_POPUP_WINDOW for more commands.
+- Invoke filter with character before mapping?
 - Figure out the size and position better.
     if wrapping splits a double-wide character
     if wrapping inserts indent
@@ -255,12 +252,19 @@ popup_getpos({id})					*popup_getpos()*
 		with these entries:
 		    col		screen column of the popup, one-based
 		    line	screen line of the popup, one-based
-		    width	width of the popup in screen cells
-		    height	height of the popup in screen cells
+		    width	width of the whole popup in screen cells
+		    height	height of the whole popup in screen cells
+		    core_col	screen column of the text box
+		    core_line	screen line of the text box
+		    core_width	width of the text box in screen cells
+		    core_height	height of the text box in screen cells
 		    visible 	one if the popup is displayed, zero if hidden
 		Note that these are the actual screen positions.  They differ
 		from the values in `popup_getoptions()` for the sizing and
 		positioning mechanism applied.
+
+		The "core_" values exclude the padding and border.
+
 		If popup window {id} is not found an empty Dict is returned.
 
 
@@ -361,11 +365,10 @@ The second argument of |popup_create()| 
 			padding uses the 'wincolor' highlight; Example: [1, 2,
 			1, 3] has 1 line of padding above, 2 columns on the
 			right, 1 line below and 3 columns on the left
-			{not implemented yet}
 	border		list with numbers, defining the border thickness
 			above/right/below/left of the popup (similar to CSS);
+			only values of zero and non-zero are recognized;
 			an empty list uses a border of 1 all around
-			{not implemented yet}
 	borderhighlight	highlight group name to use for the border
 			{not implemented yet}
 	borderchars	list with characters, defining the character to use
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -101,6 +101,38 @@ get_pos_options(win_T *wp, dict_T *dict)
     }
 }
 
+    static void
+get_padding_border(dict_T *dict, int *array, char *name, int max_val)
+{
+    dictitem_T	*di;
+
+    vim_memset(array, 0, sizeof(int) * 4);
+    di = dict_find(dict, (char_u *)name, -1);
+    if (di != NULL)
+    {
+	if (di->di_tv.v_type != VAR_LIST)
+	    emsg(_(e_listreq));
+	else
+	{
+	    list_T	*list = di->di_tv.vval.v_list;
+	    listitem_T	*li;
+	    int		i;
+	    int		nr;
+
+	    for (i = 0; i < 4; ++i)
+		array[i] = 1;
+	    if (list != NULL)
+		for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
+							 ++i, li = li->li_next)
+		{
+		    nr = (int)tv_get_number(&li->li_tv);
+		    if (nr >= 0)
+			array[i] = nr > max_val ? max_val : nr;
+		}
+	}
+    }
+}
+
 /*
  * Go through the options in "dict" and apply them to buffer "buf" displayed in
  * popup window "wp".
@@ -176,6 +208,9 @@ apply_options(win_T *wp, buf_T *buf UNUS
 	if (callback.cb_name != NULL)
 	    set_callback(&wp->w_filter_cb, &callback);
     }
+
+    get_padding_border(dict, wp->w_popup_padding, "padding", 999);
+    get_padding_border(dict, wp->w_popup_border, "border", 1);
 }
 
 /*
@@ -700,16 +735,28 @@ f_popup_getpos(typval_T *argvars, typval
     dict_T	*dict;
     int		id = (int)tv_get_number(argvars);
     win_T	*wp = find_popup_win(id);
+    int		top_extra;
+    int		left_extra;
 
     if (rettv_dict_alloc(rettv) == OK)
     {
 	if (wp == NULL)
 	    return;  // invalid {id}
+	top_extra = wp->w_popup_border[0] + wp->w_popup_padding[0];
+	left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
+
 	dict = rettv->vval.v_dict;
+
 	dict_add_number(dict, "line", wp->w_winrow + 1);
 	dict_add_number(dict, "col", wp->w_wincol + 1);
-	dict_add_number(dict, "width", wp->w_width);
-	dict_add_number(dict, "height", wp->w_height);
+	dict_add_number(dict, "width", wp->w_width + left_extra + wp->w_popup_border[1] + wp->w_popup_padding[1]);
+	dict_add_number(dict, "height", wp->w_height + top_extra + wp->w_popup_border[2] + wp->w_popup_padding[2]);
+
+	dict_add_number(dict, "core_line", wp->w_winrow + 1 + top_extra);
+	dict_add_number(dict, "core_col", wp->w_wincol + 1 + left_extra);
+	dict_add_number(dict, "core_width", wp->w_width);
+	dict_add_number(dict, "core_height", wp->w_height);
+
 	dict_add_number(dict, "visible",
 				       (wp->w_popup_flags & POPF_HIDDEN) == 0);
     }
--- a/src/screen.c
+++ b/src/screen.c
@@ -991,28 +991,6 @@ update_debug_sign(buf_T *buf, linenr_T l
 }
 #endif
 
-#ifdef FEAT_TEXT_PROP
-    static void
-update_popups(void)
-{
-    win_T   *wp;
-
-    // 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
-    // top.
-    // TODO: don't redraw every popup every time.
-    popup_reset_handled();
-    while ((wp = find_next_popup(TRUE)) != NULL)
-    {
-	// Recompute the position if the text changed.
-	if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
-	    popup_adjust_position(wp);
-
-	win_update(wp);
-    }
-}
-#endif
-
 /*
  * Get 'wincolor' attribute for window "wp".  If not set and "wp" is a popup
  * window then get the "Pmenu" highlight attribute.
@@ -1031,6 +1009,132 @@ get_wcr_attr(win_T *wp)
     return wcr_attr;
 }
 
+#ifdef FEAT_TEXT_PROP
+/*
+ * Return a string of "len" spaces in IObuff.
+ */
+    static char_u *
+get_spaces(int len)
+{
+    vim_memset(IObuff, ' ', (size_t)len);
+    IObuff[len] = NUL;
+    return IObuff;
+}
+
+    static void
+update_popups(void)
+{
+    win_T   *wp;
+    int	    top_off;
+    int	    left_off;
+    int	    total_width;
+    int	    total_height;
+    int	    popup_attr;
+    int	    row;
+
+    // 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
+    // top.
+    // TODO: don't redraw every popup every time.
+    popup_reset_handled();
+    while ((wp = find_next_popup(TRUE)) != NULL)
+    {
+	// Recompute the position if the text changed.
+	if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
+	    popup_adjust_position(wp);
+
+	// adjust w_winrow and w_wincol for border and padding, since
+	// win_update() doesn't handle them.
+	top_off = wp->w_popup_padding[0] + wp->w_popup_border[0];
+	left_off = wp->w_popup_padding[3] + wp->w_popup_border[3];
+	wp->w_winrow += top_off;
+	wp->w_wincol += left_off;
+
+	// Draw the popup text.
+	win_update(wp);
+
+	wp->w_winrow -= top_off;
+	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];
+	total_height = wp->w_popup_border[0] + wp->w_popup_padding[0]
+		+ wp->w_height + wp->w_popup_padding[2] + wp->w_popup_border[2];
+	popup_attr = get_wcr_attr(wp);
+
+	if (wp->w_popup_border[0] > 0)
+	{
+	    // top border
+	    screen_fill(wp->w_winrow, wp->w_winrow + 1,
+		    wp->w_wincol,
+		    wp->w_wincol + total_width,
+		    wp->w_popup_border[3] != 0 ? '+' : '-',
+		    '-', popup_attr);
+	    if (wp->w_popup_border[1] > 0)
+		screen_puts((char_u *)"+", wp->w_winrow,
+			wp->w_wincol + total_width - 1, popup_attr);
+	}
+
+	if (wp->w_popup_padding[0] > 0)
+	{
+	    // top padding
+	    row = wp->w_winrow + wp->w_popup_border[0];
+	    screen_fill(row, row + wp->w_popup_padding[0],
+		    wp->w_wincol + wp->w_popup_border[3],
+		    wp->w_wincol + total_width - wp->w_popup_border[1],
+							 ' ', ' ', popup_attr);
+	}
+
+	for (row = wp->w_winrow + wp->w_popup_border[0];
+		row < wp->w_winrow + total_height - wp->w_popup_border[2];
+		    ++row)
+	{
+	    // left border
+	    if (wp->w_popup_border[3] > 0)
+		screen_puts((char_u *)"|", row, wp->w_wincol, popup_attr);
+	    // left padding
+	    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);
+	    // right border
+	    if (wp->w_popup_border[1] > 0)
+		screen_puts((char_u *)"|", row,
+			wp->w_wincol + total_width - 1, popup_attr);
+	    // right padding
+	    if (wp->w_popup_padding[1] > 0)
+		screen_puts(get_spaces(wp->w_popup_padding[1]), row,
+			wp->w_wincol + wp->w_popup_border[3]
+			   + wp->w_popup_padding[3] + wp->w_width, popup_attr);
+	}
+
+	if (wp->w_popup_padding[2] > 0)
+	{
+	    // bottom padding
+	    row = wp->w_winrow + wp->w_popup_border[0]
+				       + wp->w_popup_padding[0] + wp->w_height;
+	    screen_fill(row, row + wp->w_popup_padding[2],
+		    wp->w_wincol + wp->w_popup_border[3],
+		    wp->w_wincol + total_width - wp->w_popup_border[1],
+							 ' ', ' ', popup_attr);
+	}
+
+	if (wp->w_popup_border[2] > 0)
+	{
+	    // bottom border
+	    row = wp->w_winrow + total_height - 1;
+	    screen_fill(row , row + 1,
+		    wp->w_wincol,
+		    wp->w_wincol + total_width,
+		    wp->w_popup_border[3] != 0 ? '+' : '-',
+		    '-', popup_attr);
+	    if (wp->w_popup_border[1] > 0)
+		screen_puts((char_u *)"+", row,
+			wp->w_wincol + total_width - 1, popup_attr);
+	}
+    }
+}
+#endif
+
 #if defined(FEAT_GUI) || defined(PROTO)
 /*
  * Update a single window, its status line and maybe the command line msg.
--- a/src/structs.h
+++ b/src/structs.h
@@ -2888,6 +2888,8 @@ struct window_S
     int		w_maxwidth;	    // "maxwidth" for popup window
     int		w_wantline;	    // "line" for popup window
     int		w_wantcol;	    // "col" for popup window
+    int		w_popup_padding[4]; // popup padding top/right/bot/left
+    int		w_popup_border[4];  // popup border top/right/bot/left
     varnumber_T	w_popup_last_changedtick; // b:changedtick when position was
 					  // computed
     callback_T	w_filter_cb;	    // popup filter callback
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_20.dump
@@ -0,0 +1,15 @@
+>1+0&#ffffff0| @73
+|2| |++0#0000001#ffd7ff255|-@11|+| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255@14| +0#0000000#ffffff0@4|++0#0000001#ffd7ff255|-@11|+| +0#0000000#ffffff0@18
+|3| ||+0#0000001#ffd7ff255|h|e|l@1|o| |b|o|r|d|e|r||| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255|h|e|l@1|o| |p|a|d@1|i|n|g| | +0#0000000#ffffff0@4||+0#0000001#ffd7ff255| @11||| +0#0000000#ffffff0@18
+|4| |++0#0000001#ffd7ff255|-@11|+| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255@14| +0#0000000#ffffff0@4||+0#0000001#ffd7ff255| |h|e|l@1|o| |b|o|t|h| ||| +0#0000000#ffffff0@18
+|5| @40||+0#0000001#ffd7ff255| @11||| +0#0000000#ffffff0@18
+|6| |++0#0000001#ffd7ff255|-@8| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@14| +0#0000000#ffffff0@4|++0#0000001#ffd7ff255|-@11|+| +0#0000000#ffffff0@18
+|7| ||+0#0000001#ffd7ff255|b|o|r|d|e|r| |T|L| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@3|p|a|d@1|i|n|g|s| @2| +0#0000000#ffffff0@37
+|8| @20| +0#0000001#ffd7ff255@14| +0#0000000#ffffff0@37
+|9| @20| +0#0000001#ffd7ff255@14| +0#0000000#ffffff0@37
+|1|0| @72
+|1@1| @72
+|1|2| @72
+|1|3| @72
+|1|4| @72
+@57|1|,|1| @10|T|o|p| 
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -56,6 +56,54 @@ func Test_simple_popup()
   call delete('XtestPopup')
 endfunc
 
+func Test_popup_with_border_and_padding()
+  if !CanRunVimInTerminal()
+    return
+  endif
+  call writefile([
+	\ "call setline(1, range(1, 100))",
+	\ "call popup_create('hello border', {'line': 2, 'col': 3, 'border': []})",
+	\ "call popup_create('hello padding', {'line': 2, 'col': 23, 'padding': []})",
+	\ "call popup_create('hello both', {'line': 2, 'col': 43, 'border': [], 'padding': []})",
+	\ "call popup_create('border TL', {'line': 6, 'col': 3, 'border': [1, 0, 0, 4]})",
+	\ "call popup_create('paddings', {'line': 6, 'col': 23, 'padding': [1, 3, 2, 4]})",
+	\], 'XtestPopupBorder')
+  let buf = RunVimInTerminal('-S XtestPopupBorder', {'rows': 15})
+  call VerifyScreenDump(buf, 'Test_popupwin_20', {})
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('XtestPopupBorder')
+
+  let with_border_or_padding = {
+	\ 'line': 2,
+	\ 'core_line': 3,
+	\ 'col': 3,
+	\ 'core_col': 4,
+	\ 'width': 14,
+	\ 'core_width': 12,
+	\ 'height': 3,
+	\ 'core_height': 1,
+	\ 'visible': 1}
+  let winid = popup_create('hello border', {'line': 2, 'col': 3, 'border': []})",
+  call assert_equal(with_border_or_padding, popup_getpos(winid))
+
+  let winid = popup_create('hello paddng', {'line': 2, 'col': 3, 'padding': []})
+  call assert_equal(with_border_or_padding, popup_getpos(winid))
+
+  let winid = popup_create('hello both', {'line': 3, 'col': 8, 'border': [], 'padding': []})
+  call assert_equal({
+	\ 'line': 3,
+	\ 'core_line': 5,
+	\ 'col': 8,
+	\ 'core_col': 10,
+	\ 'width': 14,
+	\ 'core_width': 10,
+	\ 'height': 5,
+	\ 'core_height': 1,
+	\ 'visible': 1}, popup_getpos(winid))
+endfunc
+
 func Test_popup_with_syntax_win_execute()
   if !CanRunVimInTerminal()
     return
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1443,
+/**/
     1442,
 /**/
     1441,