changeset 16817:069ee8dc8c8d v8.1.1410

patch 8.1.1410: popup_move() is not implemented yet commit https://github.com/vim/vim/commit/60cdb3004abe683e5e8851fa6c5d67b337df4443 Author: Bram Moolenaar <Bram@vim.org> Date: Mon May 27 21:54:10 2019 +0200 patch 8.1.1410: popup_move() is not implemented yet Problem: Popup_move() is not implemented yet. Solution: Implement it. (Yasuhiro Matsumoto, closes https://github.com/vim/vim/issues/4441) Improve the positioning and resizing.
author Bram Moolenaar <Bram@vim.org>
date Mon, 27 May 2019 22:00:07 +0200
parents b951a6ebd627
children 14c0e527b63c
files runtime/doc/popup.txt src/evalfunc.c src/popupwin.c src/proto/popupwin.pro src/screen.c src/structs.h src/testdir/dumps/Test_popupwin_05.dump src/testdir/test_popupwin.vim src/version.c
diffstat 9 files changed, 181 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -84,7 +84,7 @@ Probably 2. is the best choice.
 
 IMPLEMENTATION:
 - Code is in popupwin.c
-- Implement list of lines with text properties
+- implement popup_getposition({id}), use in tests
 - Implement filter.
 - Handle screen resize in screenalloc().
 - Make redrawing more efficient and avoid flicker.
@@ -190,7 +190,6 @@ popup_show({id})						*popup_show()*
 		For {id} see `popup_hide()`.
 
 popup_move({id}, {options})					*popup_move()*
-	  	{not implemented yet}
 		Move popup {id} to the position speficied with {options}.
 		{options} may contain the items from |popup_create()| that
 		specify the popup position: "line", "col", "pos", "maxheight",
@@ -310,10 +309,8 @@ The second argument of |popup_create()| 
 			{not implemented yet}
 	maxheight	maximum height
 	minheight	minimum height
-			{not implemented yet}
 	maxwidth	maximum width
 	minwidth	minimum width
-			{not implemented yet}
 	hidden		when TRUE the popup exists but is not displayed; use
 			`popup_show()` to unhide it.
 			{not implemented yet}
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -811,6 +811,7 @@ static struct fst
     {"popup_close",	1, 1, f_popup_close},
     {"popup_create",	2, 2, f_popup_create},
     {"popup_hide",	1, 1, f_popup_hide},
+    {"popup_move",	2, 2, f_popup_move},
     {"popup_show",	1, 1, f_popup_show},
 #endif
 #ifdef FEAT_FLOAT
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -25,10 +25,14 @@ apply_options(win_T *wp, buf_T *buf UNUS
     int	    nr;
     char_u  *str;
 
+    wp->w_minwidth = dict_get_number(dict, (char_u *)"minwidth");
+    wp->w_minheight = dict_get_number(dict, (char_u *)"minheight");
     wp->w_maxwidth = dict_get_number(dict, (char_u *)"maxwidth");
     wp->w_maxheight = dict_get_number(dict, (char_u *)"maxheight");
-    wp->w_winrow = dict_get_number(dict, (char_u *)"line");
-    wp->w_wincol = dict_get_number(dict, (char_u *)"col");
+
+    wp->w_wantline = dict_get_number(dict, (char_u *)"line");
+    wp->w_wantcol = dict_get_number(dict, (char_u *)"col");
+
     wp->w_zindex = dict_get_number(dict, (char_u *)"zindex");
 
 #if defined(FEAT_TIMERS)
@@ -145,6 +149,49 @@ add_popup_dicts(buf_T *buf, list_T *l)
 }
 
 /*
+ * Adjust the position and size of the popup to fit on the screen.
+ */
+    static void
+popup_adjust_position(win_T *wp)
+{
+    // TODO: Compute the size and position properly.
+    if (wp->w_wantline > 0)
+	wp->w_winrow = wp->w_wantline - 1;
+    else
+	// TODO: better default
+	wp->w_winrow = Rows > 5 ? Rows / 2 - 2 : 0;
+    if (wp->w_winrow >= Rows)
+	wp->w_winrow = Rows - 1;
+
+    if (wp->w_wantcol > 0)
+	wp->w_wincol = wp->w_wantcol - 1;
+    else
+	// TODO: better default
+	wp->w_wincol = Columns > 20 ? Columns / 2 - 10 : 0;
+    if (wp->w_wincol >= Columns - 3)
+	wp->w_wincol = Columns - 3;
+
+    // TODO: set width based on longest text line and the 'wrap' option
+    wp->w_width = vim_strsize(ml_get_buf(wp->w_buffer, 1, FALSE));
+    if (wp->w_minwidth > 0 && wp->w_width < wp->w_minwidth)
+	wp->w_width = wp->w_minwidth;
+    if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
+	wp->w_width = wp->w_maxwidth;
+    if (wp->w_width > Columns - wp->w_wincol)
+	wp->w_width = Columns - wp->w_wincol;
+
+    if (wp->w_height <= 1)
+	// TODO: adjust height for wrapped lines
+	wp->w_height = wp->w_buffer->b_ml.ml_line_count;
+    if (wp->w_minheight > 0 && wp->w_height < wp->w_minheight)
+	wp->w_height = wp->w_minheight;
+    if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
+	wp->w_height = wp->w_maxheight;
+    if (wp->w_height > Rows - wp->w_winrow)
+	wp->w_height = Rows - wp->w_winrow;
+}
+
+/*
  * popup_create({text}, {options})
  */
     void
@@ -241,32 +288,7 @@ f_popup_create(typval_T *argvars, typval
     if (wp->w_zindex == 0)
 	wp->w_zindex = 50;
 
-    // TODO: Compute the size and position properly.
-
-    // Default position is in middle of the screen, assuming a small popup
-    if (wp->w_winrow == 0)
-	wp->w_winrow = Rows > 5 ? Rows / 2 - 2 : 0;
-    else
-	--wp->w_winrow;  // option value is one-based
-    if (wp->w_wincol == 0)
-	wp->w_wincol = Columns > 20 ? Columns / 2 - 10 : 0;
-    else
-	--wp->w_wincol;  // option value is one-based
-
-
-    // TODO: set width based on longest text line and the 'wrap' option
-    wp->w_width = wp->w_maxwidth == 0 ? 20 : wp->w_maxwidth;
-    if (wp->w_maxwidth > 0 && wp->w_width > wp->w_maxwidth)
-	wp->w_width = wp->w_maxwidth;
-    if (wp->w_width > Columns - wp->w_wincol)
-	wp->w_width = Columns - wp->w_wincol;
-
-    // TODO: adjust height for wrapped lines
-    wp->w_height = buf->b_ml.ml_line_count;
-    if (wp->w_maxheight > 0 && wp->w_height > wp->w_maxheight)
-	wp->w_height = wp->w_maxheight;
-    if (wp->w_height > Rows - wp->w_winrow)
-	wp->w_height = Rows - wp->w_winrow;
+    popup_adjust_position(wp);
 
     wp->w_vsep_width = 0;
 
@@ -424,4 +446,45 @@ ex_popupclear(exarg_T *eap UNUSED)
     close_all_popups();
 }
 
+/*
+ * popup_move({id}, {options})
+ */
+    void
+f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
+{
+    dict_T	*d;
+    int		nr;
+    int		id = (int)tv_get_number(argvars);
+    win_T	*wp = find_popup_win(id);
+
+    if (wp == NULL)
+	return;  // invalid {id}
+
+    if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
+    {
+	emsg(_(e_dictreq));
+	return;
+    }
+    d = argvars[1].vval.v_dict;
+
+    if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0)
+	wp->w_minwidth = nr;
+    if ((nr = dict_get_number(d, (char_u *)"minheight")) > 0)
+	wp->w_minheight = nr;
+    if ((nr = dict_get_number(d, (char_u *)"maxwidth")) > 0)
+	wp->w_maxwidth = nr;
+    if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0)
+	wp->w_maxheight = nr;
+    if ((nr = dict_get_number(d, (char_u *)"line")) > 0)
+	wp->w_wantline = nr;
+    if ((nr = dict_get_number(d, (char_u *)"col")) > 0)
+	wp->w_wantcol = nr;
+    // TODO: "pos"
+
+    if (wp->w_winrow + wp->w_height >= cmdline_row)
+	clear_cmdline = TRUE;
+    popup_adjust_position(wp);
+    redraw_all_later(NOT_VALID);
+}
+
 #endif // FEAT_TEXT_PROP
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -8,4 +8,5 @@ void popup_close(int id);
 void popup_close_tabpage(tabpage_T *tp, int id);
 void close_all_popups(void);
 void ex_popupclear(exarg_T *eap);
+void f_popup_move(typval_T *argvars, typval_T *rettv);
 /* vim: set ft=c : */
--- a/src/screen.c
+++ b/src/screen.c
@@ -1034,6 +1034,24 @@ update_popups(void)
 }
 #endif
 
+/*
+ * Get 'wincolor' attribute for window "wp".  If not set and "wp" is a popup
+ * window then get the "Pmenu" highlight attribute.
+ */
+    static int
+get_wcr_attr(win_T *wp)
+{
+    int wcr_attr = 0;
+
+    if (*wp->w_p_wcr != NUL)
+	wcr_attr = syn_name2attr(wp->w_p_wcr);
+#ifdef FEAT_TEXT_PROP
+    if (bt_popup(wp->w_buffer) && wcr_attr == 0)
+	wcr_attr = HL_ATTR(HLF_PNI);
+#endif
+    return wcr_attr;
+}
+
 #if defined(FEAT_GUI) || defined(PROTO)
 /*
  * Update a single window, its status line and maybe the command line msg.
@@ -2419,13 +2437,9 @@ win_draw_end(
 {
     int		n = 0;
     int		attr = HL_ATTR(hl);
-    int		wcr_attr = 0;
-
-    if (*wp->w_p_wcr != NUL)
-    {
-	wcr_attr = syn_name2attr(wp->w_p_wcr);
-	attr = hl_combine_attr(wcr_attr, attr);
-    }
+    int		wcr_attr = get_wcr_attr(wp);
+
+    attr = hl_combine_attr(wcr_attr, attr);
 
     if (draw_margin)
     {
@@ -3115,6 +3129,8 @@ win_line(
     int		vi_attr = 0;		// attributes for Visual and incsearch
 					// highlighting
     int		wcr_attr = 0;		// attributes from 'wincolor'
+    int		win_attr = 0;		// background for whole window, except
+					// margins and "~" lines.
     int		area_attr = 0;		// attributes desired by highlighting
     int		search_attr = 0;	// attributes desired by 'hlsearch'
 #ifdef FEAT_SYN_HL
@@ -3196,7 +3212,6 @@ win_line(
 #ifdef FEAT_TERMINAL
     int		get_term_attr = FALSE;
 #endif
-    int		win_attr = 0;		// background for whole window
 
     /* draw_state: items that are drawn in sequence: */
 #define WL_START	0		/* nothing done yet */
@@ -3566,28 +3581,15 @@ win_line(
 	}
     }
 
-    if (*wp->w_p_wcr != NUL)
-    {
-	wcr_attr = syn_name2attr(wp->w_p_wcr);
-
-	// 'wincolor' highlighting for the whole window
-	if (wcr_attr != 0)
-	{
-	    win_attr = wcr_attr;
-	    area_highlighting = TRUE;
-	}
+    wcr_attr = get_wcr_attr(wp);
+    if (wcr_attr != 0)
+    {
+	win_attr = wcr_attr;
+	area_highlighting = TRUE;
     }
 #ifdef FEAT_TEXT_PROP
     if (bt_popup(wp->w_buffer))
-    {
 	screen_line_flags |= SLF_POPUP;
-
-	if (win_attr == 0)
-	{
-	    win_attr = HL_ATTR(HLF_PNI);
-	    area_highlighting = TRUE;
-	}
-    }
 #endif
 
     /*
--- a/src/structs.h
+++ b/src/structs.h
@@ -2873,8 +2873,12 @@ struct window_S
 #ifdef FEAT_TEXT_PROP
     int		w_popup_flags;	    // PFL_ values
     int		w_zindex;
+    int		w_minheight;	    // "minheight" for popup window
+    int		w_minwidth;	    // "minwidth" for popup window
     int		w_maxheight;	    // "maxheight" for popup window
     int		w_maxwidth;	    // "maxwidth" for popup window
+    int		w_wantline;	    // "line" for popup window
+    int		w_wantcol;	    // "col" for popup window
 # if defined(FEAT_TIMERS)
     timer_T	*w_popup_timer;	    // timer for closing popup window
 # endif
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_05.dump
@@ -0,0 +1,10 @@
+> +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @6|o+0#0000001#ffd7ff255|t|h|e|r| |t|a|b| @5| +0#4040ff13#ffffff0@51
+|~| @6|a+0#0000001#ffd7ff255| |c+0#ff404010&|o|m@1|e|n|t| +0#0000001&|l|i|n|e| | +0#4040ff13#ffffff0@51
+|~| @6|~+0&#ffd7ff255| @13| +0&#ffffff0@51
+|~| @73
+|~| @73
+|~| @73
+|:+0#0000000&|r|e|d|r|a|w| @49|0|,|0|-|1| @8|A|l@1| 
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -16,8 +16,8 @@ func Test_simple_popup()
 	\ "hi PopupColor2 ctermbg=lightcyan",
 	\ "hi Comment ctermfg=red",
 	\ "call prop_type_add('comment', {'highlight': 'Comment'})",
-	\ "let winid = popup_create('hello there', {'line': 3, 'col': 11, 'highlight': 'PopupColor1'})",
-	\ "let winid2 = popup_create(['another one', 'another two', 'another three'], {'line': 3, 'col': 25})",
+	\ "let winid = popup_create('hello there', {'line': 3, 'col': 11, 'minwidth': 20, 'highlight': 'PopupColor1'})",
+	\ "let winid2 = popup_create(['another one', 'another two', 'another three'], {'line': 3, 'col': 25, 'minwidth': 20})",
 	\ "call setwinvar(winid2, '&wincolor', 'PopupColor2')",
 	\], 'XtestPopup')
   let buf = RunVimInTerminal('-S XtestPopup', {'rows': 10})
@@ -25,12 +25,12 @@ func Test_simple_popup()
 
   " Add a tabpage
   call term_sendkeys(buf, ":tabnew\<CR>")
-  call term_sendkeys(buf, ":call popup_create(["
+  call term_sendkeys(buf, ":let popupwin = popup_create(["
 	\ .. "{'text': 'other tab'},"
 	\ .. "{'text': 'a comment line', 'props': [{"
-	\ .. "'col': 3, 'length': 7, 'type': 'comment'"
+	\ .. "'col': 3, 'length': 7, 'minwidth': 20, 'type': 'comment'"
 	\ .. "}]},"
-	\ .. "], {'line': 4, 'col': 9})\<CR>")
+	\ .. "], {'line': 4, 'col': 9, 'minwidth': 20})\<CR>")
   call VerifyScreenDump(buf, 'Test_popupwin_02', {})
 
   " switch back to first tabpage
@@ -41,6 +41,11 @@ func Test_simple_popup()
   call term_sendkeys(buf, ":quit!\<CR>")
   call VerifyScreenDump(buf, 'Test_popupwin_04', {})
 
+  " resize popup
+  call term_sendkeys(buf, ":call popup_move(popupwin, {'minwidth': 15, 'maxwidth': 25, 'minheight': 3, 'maxheight': 5})\<CR>")
+  call term_sendkeys(buf, ":redraw\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_05', {})
+
   " clean up
   call StopVimInTerminal(buf)
   call delete('XtestPopup')
@@ -56,6 +61,7 @@ func Test_popup_time()
   call popup_create('world', {
 	\ 'line': 1,
 	\ 'col': 1,
+	\ 'minwidth': 20,
 	\ 'time': 500,
 	\})
   redraw
@@ -70,6 +76,7 @@ func Test_popup_time()
   call popup_create('on the command line', {
 	\ 'line': &lines,
 	\ 'col': 10,
+	\ 'minwidth': 20,
 	\ 'time': 500,
 	\})
   redraw
@@ -91,6 +98,7 @@ func Test_popup_hide()
   let winid = popup_create('world', {
 	\ 'line': 1,
 	\ 'col': 1,
+	\ 'minwidth': 20,
 	\})
   redraw
   let line = join(map(range(1, 5), 'screenstring(1, v:val)'), '')
@@ -121,3 +129,33 @@ func Test_popup_hide()
 
   bwipe!
 endfunc
+
+func Test_popup_move()
+  topleft vnew
+  call setline(1, 'hello')
+
+  let winid = popup_create('world', {
+	\ 'line': 1,
+	\ 'col': 1,
+	\ 'minwidth': 20,
+	\})
+  redraw
+  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
+  call assert_equal('world ', line)
+
+  call popup_move(winid, {'line': 2, 'col': 2})
+  redraw
+  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
+  call assert_equal('hello ', line)
+  let line = join(map(range(1, 6), 'screenstring(2, v:val)'), '')
+  call assert_equal('~world', line)
+
+  call popup_move(winid, {'line': 1})
+  redraw
+  let line = join(map(range(1, 6), 'screenstring(1, v:val)'), '')
+  call assert_equal('hworld', line)
+
+  call popup_close(winid)
+
+  bwipe!
+endfunc
--- 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 */
 /**/
+    1410,
+/**/
     1409,
 /**/
     1408,