summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/popup.txt35
-rw-r--r--src/evalfunc.c2
-rw-r--r--src/popupwin.c168
-rw-r--r--src/proto/popupwin.pro2
-rw-r--r--src/screen.c11
-rw-r--r--src/testdir/dumps/Test_popupwin_drag_01.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_drag_02.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_menu_01.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_menu_02.dump10
-rw-r--r--src/testdir/dumps/Test_popupwin_menu_03.dump10
-rw-r--r--src/testdir/test_popupwin.vim58
-rw-r--r--src/version.c2
12 files changed, 276 insertions, 52 deletions
diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt
index 02c66c3b3..a6dab8b91 100644
--- a/runtime/doc/popup.txt
+++ b/runtime/doc/popup.txt
@@ -89,7 +89,7 @@ that it is in.
TODO:
- Why does 'nrformats' leak from the popup window buffer???
- Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
- Use NOT_IN_POPUP_WINDOW for more commands.
+ Use ERROR_IF_POPUP_WINDOW for more commands.
- Add 'balloonpopup': instead of showing text, let the callback open a popup
window and return the window ID. The popup will then be closed when the
mouse moves, except when it moves inside the popup.
@@ -109,8 +109,6 @@ TODO:
- When the lines do not fit show a scrollbar (like in the popup menu).
Use the mouse wheel for scrolling.
- Implement:
- popup_filter_menu({id}, {key})
- popup_menu({text}, {options})
popup_setoptions({id}, {options})
hidden option
tabpage option with number
@@ -220,12 +218,20 @@ popup_dialog({text}, {options}) *popup_dialog()*
popup_filter_menu({id}, {key}) *popup_filter_menu()*
- {not implemented yet}
- Filter that can be used for a popup. It handles the cursor
- keys to move the selected index in the popup. Space and Enter
- can be used to select an item. Invokes the "callback" of the
- popup menu with the index of the selected line as the second
- argument.
+ Filter that can be used for a popup. These keys can be used:
+ j <Down> select item below
+ k <Up> select item above
+ <Space> <Enter> accept current selection
+ x Esc CTRL-C cancel the menu
+ Other keys are ignored.
+
+ A match is set on that line to highlight it, see
+ |popup_menu()|.
+
+ When the current selection is accepted the "callback" of the
+ popup menu is invoked with the index of the selected line as
+ the second argument. The first entry has index one.
+ Cancelling the menu invokes the callback with -1.
popup_filter_yesno({id}, {key}) *popup_filter_yesno()*
@@ -279,7 +285,6 @@ popup_hide({id}) *popup_hide()*
popup_menu({text}, {options}) *popup_menu()*
- {not implemented yet}
Show the {text} near the cursor, handle selecting one of the
items with cursorkeys, and close it an item is selected with
Space or Enter. {text} should have multiple lines to make this
@@ -287,11 +292,16 @@ popup_menu({text}, {options}) *popup_menu()*
call popup_create({text}, {
\ 'pos': 'center',
\ 'zindex': 200,
+ \ 'drag': 1,
\ 'wrap': 0,
\ 'border': [],
+ \ 'padding': [],
\ 'filter': 'popup_filter_menu',
\ })
-< Use {options} to change the properties. Should at least set
+< The current line is highlighted with a match using
+ PopupSelected, or |PmenuSel| if that is not defined.
+
+ Use {options} to change the properties. Should at least set
"callback" to a function that handles the selected item.
@@ -320,7 +330,8 @@ popup_notification({text}, {options}) *popup_notification()*
\ })
< The PopupNotification highlight group is used instead of
WarningMsg if it is defined.
-< The position will be adjusted to avoid overlap with other
+
+ The position will be adjusted to avoid overlap with other
notifications.
Use {options} to change the properties.
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 8c8bb4d63..b98c7e72e 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -816,10 +816,12 @@ static struct fst
{"popup_close", 1, 2, f_popup_close},
{"popup_create", 2, 2, f_popup_create},
{"popup_dialog", 2, 2, f_popup_dialog},
+ {"popup_filter_menu", 2, 2, f_popup_filter_menu},
{"popup_filter_yesno", 2, 2, f_popup_filter_yesno},
{"popup_getoptions", 1, 1, f_popup_getoptions},
{"popup_getpos", 1, 1, f_popup_getpos},
{"popup_hide", 1, 1, f_popup_hide},
+ {"popup_menu", 2, 2, f_popup_menu},
{"popup_move", 2, 2, f_popup_move},
{"popup_notification", 2, 2, f_popup_notification},
{"popup_settext", 2, 2, f_popup_settext},
diff --git a/src/popupwin.c b/src/popupwin.c
index ea1a1e1e7..ccf1f6685 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -651,7 +651,11 @@ popup_adjust_position(win_T *wp)
if (wp->w_width > maxwidth)
wp->w_width = maxwidth;
if (center_hor)
- wp->w_wincol = (Columns - wp->w_width) / 2;
+ {
+ wp->w_wincol = (Columns - wp->w_width - extra_width) / 2;
+ if (wp->w_wincol < 0)
+ wp->w_wincol = 0;
+ }
else if (wp->w_popup_pos == POPPOS_BOTRIGHT
|| wp->w_popup_pos == POPPOS_TOPRIGHT)
{
@@ -671,7 +675,11 @@ popup_adjust_position(win_T *wp)
wp->w_height = Rows - wp->w_winrow;
if (center_vert)
- wp->w_winrow = (Rows - wp->w_height) / 2;
+ {
+ wp->w_winrow = (Rows - wp->w_height - extra_height) / 2;
+ if (wp->w_winrow < 0)
+ wp->w_winrow = 0;
+ }
else if (wp->w_popup_pos == POPPOS_BOTRIGHT
|| wp->w_popup_pos == POPPOS_BOTLEFT)
{
@@ -702,7 +710,8 @@ typedef enum
TYPE_NORMAL,
TYPE_ATCURSOR,
TYPE_NOTIFICATION,
- TYPE_DIALOG
+ TYPE_DIALOG,
+ TYPE_MENU
} create_type_T;
/*
@@ -751,7 +760,7 @@ popup_set_buffer_text(buf_T *buf, typval_T text)
* popup_create({text}, {options})
* popup_atcursor({text}, {options})
*/
- static void
+ static win_T *
popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
{
win_T *wp;
@@ -764,25 +773,25 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
&& !(argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list != NULL))
{
emsg(_(e_listreq));
- return;
+ return NULL;
}
if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
{
emsg(_(e_dictreq));
- return;
+ return NULL;
}
d = argvars[1].vval.v_dict;
// Create the window and buffer.
wp = win_alloc_popup_win();
if (wp == NULL)
- return;
+ return NULL;
rettv->vval.v_number = wp->w_id;
wp->w_popup_pos = POPPOS_TOPLEFT;
buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
if (buf == NULL)
- return;
+ return NULL;
ml_open(buf);
win_init_popup_win(wp, buf);
@@ -898,7 +907,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
OPT_FREE|OPT_LOCAL, 0);
}
- if (type == TYPE_DIALOG)
+ if (type == TYPE_DIALOG || type == TYPE_MENU)
{
int i;
@@ -912,6 +921,20 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
}
}
+ if (type == TYPE_MENU)
+ {
+ typval_T tv;
+ callback_T callback;
+
+ tv.v_type = VAR_STRING;
+ tv.vval.v_string = (char_u *)"popup_filter_menu";
+ callback = get_callback(&tv);
+ if (callback.cb_name != NULL)
+ set_callback(&wp->w_filter_cb, &callback);
+
+ wp->w_p_wrap = 0;
+ }
+
// Deal with options.
apply_options(wp, buf, argvars[1].vval.v_dict);
@@ -924,6 +947,8 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
redraw_all_later(NOT_VALID);
popup_mask_refresh = TRUE;
+
+ return wp;
}
/*
@@ -1000,6 +1025,93 @@ popup_close_and_callback(win_T *wp, typval_T *arg)
}
/*
+ * In a filter: check if the typed key is a mouse event that is used for
+ * dragging the popup.
+ */
+ static void
+filter_handle_drag(win_T *wp, int c, typval_T *rettv)
+{
+ int row = mouse_row;
+ int col = mouse_col;
+
+ if (wp->w_popup_drag
+ && is_mouse_key(c)
+ && (wp == popup_dragwin
+ || wp == mouse_find_win(&row, &col, FIND_POPUP)))
+ // do not consume the key, allow for dragging the popup
+ rettv->vval.v_number = 0;
+}
+
+ static void
+popup_highlight_curline(win_T *wp)
+{
+ int id;
+ char buf[100];
+
+ match_delete(wp, 1, FALSE);
+
+ id = syn_name2id((char_u *)"PopupSelected");
+ vim_snprintf(buf, sizeof(buf), "\\%%%dl.*", (int)wp->w_cursor.lnum);
+ match_add(wp, (char_u *)(id == 0 ? "PmenuSel" : "PopupSelected"),
+ (char_u *)buf, 10, 1, NULL, NULL);
+}
+
+/*
+ * popup_filter_menu({text}, {options})
+ */
+ void
+f_popup_filter_menu(typval_T *argvars, typval_T *rettv)
+{
+ int id = tv_get_number(&argvars[0]);
+ win_T *wp = win_id2wp(id);
+ char_u *key = tv_get_string(&argvars[1]);
+ typval_T res;
+ int c;
+ linenr_T old_lnum;
+
+ // If the popup has been closed do not consume the key.
+ if (wp == NULL)
+ return;
+
+ c = *key;
+ if (c == K_SPECIAL && key[1] != NUL)
+ c = TO_SPECIAL(key[1], key[2]);
+
+ // consume all keys until done
+ rettv->vval.v_number = 1;
+ res.v_type = VAR_NUMBER;
+
+ old_lnum = wp->w_cursor.lnum;
+ if ((c == 'k' || c == 'K' || c == K_UP) && wp->w_cursor.lnum > 1)
+ --wp->w_cursor.lnum;
+ if ((c == 'j' || c == 'J' || c == K_DOWN)
+ && wp->w_cursor.lnum < wp->w_buffer->b_ml.ml_line_count)
+ ++wp->w_cursor.lnum;
+ if (old_lnum != wp->w_cursor.lnum)
+ {
+ popup_highlight_curline(wp);
+ return;
+ }
+
+ if (c == 'x' || c == 'X' || c == ESC || c == Ctrl_C)
+ {
+ // Cancelled, invoke callback with -1
+ res.vval.v_number = -1;
+ popup_close_and_callback(wp, &res);
+ return;
+ }
+ if (c == ' ' || c == K_KENTER || c == CAR || c == NL)
+ {
+ // Invoke callback with current index.
+ res.vval.v_number = wp->w_cursor.lnum;
+ popup_close_and_callback(wp, &res);
+ return;
+ }
+
+ filter_handle_drag(wp, c, rettv);
+}
+
+/*
* popup_filter_yesno({text}, {options})
*/
void
@@ -1009,36 +1121,26 @@ f_popup_filter_yesno(typval_T *argvars, typval_T *rettv)
win_T *wp = win_id2wp(id);
char_u *key = tv_get_string(&argvars[1]);
typval_T res;
+ int c;
// If the popup has been closed don't consume the key.
if (wp == NULL)
return;
+ c = *key;
+ if (c == K_SPECIAL && key[1] != NUL)
+ c = TO_SPECIAL(key[1], key[2]);
+
// consume all keys until done
rettv->vval.v_number = 1;
- if (STRCMP(key, "y") == 0 || STRCMP(key, "Y") == 0)
+ if (c == 'y' || c == 'Y')
res.vval.v_number = 1;
- else if (STRCMP(key, "n") == 0 || STRCMP(key, "N") == 0
- || STRCMP(key, "x") == 0 || STRCMP(key, "X") == 0
- || STRCMP(key, "\x1b") == 0)
+ else if (c == 'n' || c == 'N' || c == 'x' || c == 'X' || c == ESC)
res.vval.v_number = 0;
else
{
- int c = *key;
- int row = mouse_row;
- int col = mouse_col;
-
- if (c == K_SPECIAL && key[1] != NUL)
- c = TO_SPECIAL(key[1], key[2]);
- if (wp->w_popup_drag
- && is_mouse_key(c)
- && (wp == popup_dragwin
- || wp == mouse_find_win(&row, &col, FIND_POPUP)))
- // allow for dragging the popup
- rettv->vval.v_number = 0;
-
- // ignore this key
+ filter_handle_drag(wp, c, rettv);
return;
}
@@ -1057,6 +1159,18 @@ f_popup_dialog(typval_T *argvars, typval_T *rettv)
}
/*
+ * popup_menu({text}, {options})
+ */
+ void
+f_popup_menu(typval_T *argvars, typval_T *rettv)
+{
+ win_T *wp = popup_create(argvars, rettv, TYPE_MENU);
+
+ if (wp != NULL)
+ popup_highlight_curline(wp);
+}
+
+/*
* popup_notification({text}, {options})
*/
void
diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro
index 1eb2c102a..fefb03142 100644
--- a/src/proto/popupwin.pro
+++ b/src/proto/popupwin.pro
@@ -8,8 +8,10 @@ void popup_adjust_position(win_T *wp);
void f_popup_clear(typval_T *argvars, typval_T *rettv);
void f_popup_create(typval_T *argvars, typval_T *rettv);
void f_popup_atcursor(typval_T *argvars, typval_T *rettv);
+void f_popup_filter_menu(typval_T *argvars, typval_T *rettv);
void f_popup_filter_yesno(typval_T *argvars, typval_T *rettv);
void f_popup_dialog(typval_T *argvars, typval_T *rettv);
+void f_popup_menu(typval_T *argvars, typval_T *rettv);
void f_popup_notification(typval_T *argvars, typval_T *rettv);
void f_popup_close(typval_T *argvars, typval_T *rettv);
void f_popup_hide(typval_T *argvars, typval_T *rettv);
diff --git a/src/screen.c b/src/screen.c
index 8708fd7c2..988626741 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -4183,7 +4183,7 @@ win_line(
*/
v = (long)(ptr - line);
cur = wp->w_match_head;
- shl_flag = (screen_line_flags & SLF_POPUP);
+ shl_flag = FALSE;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE
@@ -4193,6 +4193,8 @@ win_line(
{
shl = &search_hl;
shl_flag = TRUE;
+ if (screen_line_flags & SLF_POPUP)
+ continue; // do not use search_hl
}
else
shl = &cur->hl;
@@ -4272,9 +4274,9 @@ win_line(
/* Use attributes from match with highest priority among
* 'search_hl' and the match list. */
- search_attr = search_hl.attr_cur;
cur = wp->w_match_head;
shl_flag = FALSE;
+ search_attr = 0;
while (cur != NULL || shl_flag == FALSE)
{
if (shl_flag == FALSE
@@ -4284,6 +4286,8 @@ win_line(
{
shl = &search_hl;
shl_flag = TRUE;
+ if (screen_line_flags & SLF_POPUP)
+ continue; // do not use search_hl
}
else
shl = &cur->hl;
@@ -5564,7 +5568,6 @@ win_line(
{
/* Use attributes from match with highest priority among
* 'search_hl' and the match list. */
- char_attr = search_hl.attr;
cur = wp->w_match_head;
shl_flag = FALSE;
while (cur != NULL || shl_flag == FALSE)
@@ -5576,6 +5579,8 @@ win_line(
{
shl = &search_hl;
shl_flag = TRUE;
+ if (screen_line_flags & SLF_POPUP)
+ continue; // do not use search_hl
}
else
shl = &cur->hl;
diff --git a/src/testdir/dumps/Test_popupwin_drag_01.dump b/src/testdir/dumps/Test_popupwin_drag_01.dump
index e47fbc4fa..c8867e3ff 100644
--- a/src/testdir/dumps/Test_popupwin_drag_01.dump
+++ b/src/testdir/dumps/Test_popupwin_drag_01.dump
@@ -3,8 +3,8 @@
|3| @73
|4| @73
|5| @73
-|6| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#0000000#ffffff0@32
-|7| @32|║+0#0000001#ffd7ff255|1@3| @1|║| +0#0000000#ffffff0@32
-|8| @32|║+0#0000001#ffd7ff255|2@5|║| +0#0000000#ffffff0@32
-|9| @32|║+0#0000001#ffd7ff255|3@4| |║| +0#0000000#ffffff0@32
-@34|╚+0#0000001#ffd7ff255|═@5|╝| +0#0000000#ffffff0@14|1|,|1| @10|T|o|p|
+|6| @31|╔+0#0000001#ffd7ff255|═@5|╗| +0#0000000#ffffff0@33
+|7| @31|║+0#0000001#ffd7ff255|1@3| @1|║| +0#0000000#ffffff0@33
+|8| @31|║+0#0000001#ffd7ff255|2@5|║| +0#0000000#ffffff0@33
+|9| @31|║+0#0000001#ffd7ff255|3@4| |║| +0#0000000#ffffff0@33
+@33|╚+0#0000001#ffd7ff255|═@5|╝| +0#0000000#ffffff0@15|1|,|1| @10|T|o|p|
diff --git a/src/testdir/dumps/Test_popupwin_drag_02.dump b/src/testdir/dumps/Test_popupwin_drag_02.dump
index 7e8c30ee6..f03ecb847 100644
--- a/src/testdir/dumps/Test_popupwin_drag_02.dump
+++ b/src/testdir/dumps/Test_popupwin_drag_02.dump
@@ -1,9 +1,9 @@
>1+0&#ffffff0| @73
-|2| @32|╔+0#0000001#ffd7ff255|═@5|╗| +0#0000000#ffffff0@32
-|3| @32|║+0#0000001#ffd7ff255|1@3| @1|║| +0#0000000#ffffff0@32
-|4| @32|║+0#0000001#ffd7ff255|2@5|║| +0#0000000#ffffff0@32
-|5| @32|║+0#0000001#ffd7ff255|3@4| |║| +0#0000000#ffffff0@32
-|6| @32|╚+0#0000001#ffd7ff255|═@5|╝| +0#0000000#ffffff0@32
+|2| @31|╔+0#0000001#ffd7ff255|═@5|╗| +0#0000000#ffffff0@33
+|3| @31|║+0#0000001#ffd7ff255|1@3| @1|║| +0#0000000#ffffff0@33
+|4| @31|║+0#0000001#ffd7ff255|2@5|║| +0#0000000#ffffff0@33
+|5| @31|║+0#0000001#ffd7ff255|3@4| |║| +0#0000000#ffffff0@33
+|6| @31|╚+0#0000001#ffd7ff255|═@5|╝| +0#0000000#ffffff0@33
|7| @73
|8| @73
|9| @73
diff --git a/src/testdir/dumps/Test_popupwin_menu_01.dump b/src/testdir/dumps/Test_popupwin_menu_01.dump
new file mode 100644
index 000000000..debd96250
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_menu_01.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @30|╔+0#0000001#ffd7ff255|═@8|╗| +0#0000000#ffffff0@31
+|3| @30|║+0#0000001#ffd7ff255| @8|║| +0#0000000#ffffff0@31
+|4| @30|║+0#0000001#ffd7ff255| |o+0#0000000#5fd7ff255|n|e| +0#0000001#ffd7ff255@4|║| +0#0000000#ffffff0@31
+|5| @30|║+0#0000001#ffd7ff255| |t|w|o| @4|║| +0#0000000#ffffff0@31
+|6| @30|║+0#0000001#ffd7ff255| |a|n|o|t|h|e|r| |║| +0#0000000#ffffff0@31
+|7| @30|║+0#0000001#ffd7ff255| @8|║| +0#0000000#ffffff0@31
+|8| @30|╚+0#0000001#ffd7ff255|═@8|╝| +0#0000000#ffffff0@31
+|9| @73
+@57|1|,|1| @10|T|o|p|
diff --git a/src/testdir/dumps/Test_popupwin_menu_02.dump b/src/testdir/dumps/Test_popupwin_menu_02.dump
new file mode 100644
index 000000000..2495edfef
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_menu_02.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @30|╔+0#0000001#ffd7ff255|═@8|╗| +0#0000000#ffffff0@31
+|3| @30|║+0#0000001#ffd7ff255| @8|║| +0#0000000#ffffff0@31
+|4| @30|║+0#0000001#ffd7ff255| |o|n|e| @4|║| +0#0000000#ffffff0@31
+|5| @30|║+0#0000001#ffd7ff255| |t|w|o| @4|║| +0#0000000#ffffff0@31
+|6| @30|║+0#0000001#ffd7ff255| |a+0#0000000#5fd7ff255|n|o|t|h|e|r| +0#0000001#ffd7ff255|║| +0#0000000#ffffff0@31
+|7| @30|║+0#0000001#ffd7ff255| @8|║| +0#0000000#ffffff0@31
+|8| @30|╚+0#0000001#ffd7ff255|═@8|╝| +0#0000000#ffffff0@31
+|9| @73
+@57|1|,|1| @10|T|o|p|
diff --git a/src/testdir/dumps/Test_popupwin_menu_03.dump b/src/testdir/dumps/Test_popupwin_menu_03.dump
new file mode 100644
index 000000000..1aa7fe805
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_menu_03.dump
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @73
+|5| @73
+|6| @73
+|7| @73
+|8| @73
+|9| @73
+|s|e|l|e|c|t|e|d| |3| @46|1|,|1| @10|T|o|p|
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index ebe24004a..477cda38a 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -902,6 +902,64 @@ func Test_popup_dialog()
delfunc QuitCallback
endfunc
+func ShowMenu(key, result)
+ let s:cb_res = 999
+ let winid = popup_menu(['one', 'two', 'something else'], {
+ \ 'callback': 'QuitCallback',
+ \ })
+ redraw
+ call feedkeys(a:key, "xt")
+ call assert_equal(winid, s:cb_winid)
+ call assert_equal(a:result, s:cb_res)
+endfunc
+
+func Test_popup_menu()
+ func QuitCallback(id, res)
+ let s:cb_winid = a:id
+ let s:cb_res = a:res
+ endfunc
+
+ let winid = ShowMenu(" ", 1)
+ let winid = ShowMenu("j \<CR>", 2)
+ let winid = ShowMenu("JjK \<CR>", 2)
+ let winid = ShowMenu("jjjjjj ", 3)
+ let winid = ShowMenu("kkk ", 1)
+ let winid = ShowMenu("x", -1)
+ let winid = ShowMenu("X", -1)
+ let winid = ShowMenu("\<Esc>", -1)
+ let winid = ShowMenu("\<C-C>", -1)
+
+ delfunc QuitCallback
+endfunc
+
+func Test_popup_menu_screenshot()
+ if !CanRunVimInTerminal()
+ throw 'Skipped: cannot make screendumps'
+ endif
+
+ let lines =<< trim END
+ call setline(1, range(1, 20))
+ hi PopupSelected ctermbg=lightblue
+ call popup_menu(['one', 'two', 'another'], {'callback': 'MenuDone'})
+ func MenuDone(id, res)
+ echomsg "selected " .. a:res
+ endfunc
+ END
+ call writefile(lines, 'XtestPopupMenu')
+ let buf = RunVimInTerminal('-S XtestPopupMenu', {'rows': 10})
+ call VerifyScreenDump(buf, 'Test_popupwin_menu_01', {})
+
+ call term_sendkeys(buf, "jj")
+ call VerifyScreenDump(buf, 'Test_popupwin_menu_02', {})
+
+ call term_sendkeys(buf, " ")
+ call VerifyScreenDump(buf, 'Test_popupwin_menu_03', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XtestPopupMenu')
+endfunc
+
func Test_popup_close_callback()
func PopupDone(id, result)
let g:result = a:result
diff --git a/src/version.c b/src/version.c
index f6bb71c89..a59803bb9 100644
--- 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 */
/**/
+ 1558,
+/**/
1557,
/**/
1556,