Skip to content

Commit a730e55

Browse files
committed
patch 8.1.1558: popup_menu() and popup_filter_menu() are not implemented yet
Problem: Popup_menu() and popup_filter_menu() are not implemented yet. Solution: Implement the functions. Fix that centering didn't take the border and padding into account.
1 parent 983f2f1 commit a730e55

12 files changed

Lines changed: 276 additions & 52 deletions

runtime/doc/popup.txt

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ that it is in.
8989
TODO:
9090
- Why does 'nrformats' leak from the popup window buffer???
9191
- Disable commands, feedkeys(), CTRL-W, etc. in a popup window.
92-
Use NOT_IN_POPUP_WINDOW for more commands.
92+
Use ERROR_IF_POPUP_WINDOW for more commands.
9393
- Add 'balloonpopup': instead of showing text, let the callback open a popup
9494
window and return the window ID. The popup will then be closed when the
9595
mouse moves, except when it moves inside the popup.
@@ -109,8 +109,6 @@ TODO:
109109
- When the lines do not fit show a scrollbar (like in the popup menu).
110110
Use the mouse wheel for scrolling.
111111
- Implement:
112-
popup_filter_menu({id}, {key})
113-
popup_menu({text}, {options})
114112
popup_setoptions({id}, {options})
115113
hidden option
116114
tabpage option with number
@@ -220,12 +218,20 @@ popup_dialog({text}, {options}) *popup_dialog()*
220218

221219

222220
popup_filter_menu({id}, {key}) *popup_filter_menu()*
223-
{not implemented yet}
224-
Filter that can be used for a popup. It handles the cursor
225-
keys to move the selected index in the popup. Space and Enter
226-
can be used to select an item. Invokes the "callback" of the
227-
popup menu with the index of the selected line as the second
228-
argument.
221+
Filter that can be used for a popup. These keys can be used:
222+
j <Down> select item below
223+
k <Up> select item above
224+
<Space> <Enter> accept current selection
225+
x Esc CTRL-C cancel the menu
226+
Other keys are ignored.
227+
228+
A match is set on that line to highlight it, see
229+
|popup_menu()|.
230+
231+
When the current selection is accepted the "callback" of the
232+
popup menu is invoked with the index of the selected line as
233+
the second argument. The first entry has index one.
234+
Cancelling the menu invokes the callback with -1.
229235

230236

231237
popup_filter_yesno({id}, {key}) *popup_filter_yesno()*
@@ -279,19 +285,23 @@ popup_hide({id}) *popup_hide()*
279285

280286

281287
popup_menu({text}, {options}) *popup_menu()*
282-
{not implemented yet}
283288
Show the {text} near the cursor, handle selecting one of the
284289
items with cursorkeys, and close it an item is selected with
285290
Space or Enter. {text} should have multiple lines to make this
286291
useful. This works like: >
287292
call popup_create({text}, {
288293
\ 'pos': 'center',
289294
\ 'zindex': 200,
295+
\ 'drag': 1,
290296
\ 'wrap': 0,
291297
\ 'border': [],
298+
\ 'padding': [],
292299
\ 'filter': 'popup_filter_menu',
293300
\ })
294-
< Use {options} to change the properties. Should at least set
301+
< The current line is highlighted with a match using
302+
PopupSelected, or |PmenuSel| if that is not defined.
303+
304+
Use {options} to change the properties. Should at least set
295305
"callback" to a function that handles the selected item.
296306

297307

@@ -320,7 +330,8 @@ popup_notification({text}, {options}) *popup_notification()*
320330
\ })
321331
< The PopupNotification highlight group is used instead of
322332
WarningMsg if it is defined.
323-
< The position will be adjusted to avoid overlap with other
333+
334+
The position will be adjusted to avoid overlap with other
324335
notifications.
325336
Use {options} to change the properties.
326337

src/evalfunc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,10 +816,12 @@ static struct fst
816816
{"popup_close", 1, 2, f_popup_close},
817817
{"popup_create", 2, 2, f_popup_create},
818818
{"popup_dialog", 2, 2, f_popup_dialog},
819+
{"popup_filter_menu", 2, 2, f_popup_filter_menu},
819820
{"popup_filter_yesno", 2, 2, f_popup_filter_yesno},
820821
{"popup_getoptions", 1, 1, f_popup_getoptions},
821822
{"popup_getpos", 1, 1, f_popup_getpos},
822823
{"popup_hide", 1, 1, f_popup_hide},
824+
{"popup_menu", 2, 2, f_popup_menu},
823825
{"popup_move", 2, 2, f_popup_move},
824826
{"popup_notification", 2, 2, f_popup_notification},
825827
{"popup_settext", 2, 2, f_popup_settext},

src/popupwin.c

Lines changed: 141 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,11 @@ popup_adjust_position(win_T *wp)
651651
if (wp->w_width > maxwidth)
652652
wp->w_width = maxwidth;
653653
if (center_hor)
654-
wp->w_wincol = (Columns - wp->w_width) / 2;
654+
{
655+
wp->w_wincol = (Columns - wp->w_width - extra_width) / 2;
656+
if (wp->w_wincol < 0)
657+
wp->w_wincol = 0;
658+
}
655659
else if (wp->w_popup_pos == POPPOS_BOTRIGHT
656660
|| wp->w_popup_pos == POPPOS_TOPRIGHT)
657661
{
@@ -671,7 +675,11 @@ popup_adjust_position(win_T *wp)
671675
wp->w_height = Rows - wp->w_winrow;
672676

673677
if (center_vert)
674-
wp->w_winrow = (Rows - wp->w_height) / 2;
678+
{
679+
wp->w_winrow = (Rows - wp->w_height - extra_height) / 2;
680+
if (wp->w_winrow < 0)
681+
wp->w_winrow = 0;
682+
}
675683
else if (wp->w_popup_pos == POPPOS_BOTRIGHT
676684
|| wp->w_popup_pos == POPPOS_BOTLEFT)
677685
{
@@ -702,7 +710,8 @@ typedef enum
702710
TYPE_NORMAL,
703711
TYPE_ATCURSOR,
704712
TYPE_NOTIFICATION,
705-
TYPE_DIALOG
713+
TYPE_DIALOG,
714+
TYPE_MENU
706715
} create_type_T;
707716

708717
/*
@@ -751,7 +760,7 @@ popup_set_buffer_text(buf_T *buf, typval_T text)
751760
* popup_create({text}, {options})
752761
* popup_atcursor({text}, {options})
753762
*/
754-
static void
763+
static win_T *
755764
popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
756765
{
757766
win_T *wp;
@@ -764,25 +773,25 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
764773
&& !(argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list != NULL))
765774
{
766775
emsg(_(e_listreq));
767-
return;
776+
return NULL;
768777
}
769778
if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)
770779
{
771780
emsg(_(e_dictreq));
772-
return;
781+
return NULL;
773782
}
774783
d = argvars[1].vval.v_dict;
775784

776785
// Create the window and buffer.
777786
wp = win_alloc_popup_win();
778787
if (wp == NULL)
779-
return;
788+
return NULL;
780789
rettv->vval.v_number = wp->w_id;
781790
wp->w_popup_pos = POPPOS_TOPLEFT;
782791

783792
buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
784793
if (buf == NULL)
785-
return;
794+
return NULL;
786795
ml_open(buf);
787796

788797
win_init_popup_win(wp, buf);
@@ -898,7 +907,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
898907
OPT_FREE|OPT_LOCAL, 0);
899908
}
900909

901-
if (type == TYPE_DIALOG)
910+
if (type == TYPE_DIALOG || type == TYPE_MENU)
902911
{
903912
int i;
904913

@@ -912,6 +921,20 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
912921
}
913922
}
914923

924+
if (type == TYPE_MENU)
925+
{
926+
typval_T tv;
927+
callback_T callback;
928+
929+
tv.v_type = VAR_STRING;
930+
tv.vval.v_string = (char_u *)"popup_filter_menu";
931+
callback = get_callback(&tv);
932+
if (callback.cb_name != NULL)
933+
set_callback(&wp->w_filter_cb, &callback);
934+
935+
wp->w_p_wrap = 0;
936+
}
937+
915938
// Deal with options.
916939
apply_options(wp, buf, argvars[1].vval.v_dict);
917940

@@ -924,6 +947,8 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
924947

925948
redraw_all_later(NOT_VALID);
926949
popup_mask_refresh = TRUE;
950+
951+
return wp;
927952
}
928953

929954
/*
@@ -999,6 +1024,93 @@ popup_close_and_callback(win_T *wp, typval_T *arg)
9991024
popup_close(id);
10001025
}
10011026

1027+
/*
1028+
* In a filter: check if the typed key is a mouse event that is used for
1029+
* dragging the popup.
1030+
*/
1031+
static void
1032+
filter_handle_drag(win_T *wp, int c, typval_T *rettv)
1033+
{
1034+
int row = mouse_row;
1035+
int col = mouse_col;
1036+
1037+
if (wp->w_popup_drag
1038+
&& is_mouse_key(c)
1039+
&& (wp == popup_dragwin
1040+
|| wp == mouse_find_win(&row, &col, FIND_POPUP)))
1041+
// do not consume the key, allow for dragging the popup
1042+
rettv->vval.v_number = 0;
1043+
}
1044+
1045+
static void
1046+
popup_highlight_curline(win_T *wp)
1047+
{
1048+
int id;
1049+
char buf[100];
1050+
1051+
match_delete(wp, 1, FALSE);
1052+
1053+
id = syn_name2id((char_u *)"PopupSelected");
1054+
vim_snprintf(buf, sizeof(buf), "\\%%%dl.*", (int)wp->w_cursor.lnum);
1055+
match_add(wp, (char_u *)(id == 0 ? "PmenuSel" : "PopupSelected"),
1056+
(char_u *)buf, 10, 1, NULL, NULL);
1057+
}
1058+
1059+
/*
1060+
* popup_filter_menu({text}, {options})
1061+
*/
1062+
void
1063+
f_popup_filter_menu(typval_T *argvars, typval_T *rettv)
1064+
{
1065+
int id = tv_get_number(&argvars[0]);
1066+
win_T *wp = win_id2wp(id);
1067+
char_u *key = tv_get_string(&argvars[1]);
1068+
typval_T res;
1069+
int c;
1070+
linenr_T old_lnum;
1071+
1072+
// If the popup has been closed do not consume the key.
1073+
if (wp == NULL)
1074+
return;
1075+
1076+
c = *key;
1077+
if (c == K_SPECIAL && key[1] != NUL)
1078+
c = TO_SPECIAL(key[1], key[2]);
1079+
1080+
// consume all keys until done
1081+
rettv->vval.v_number = 1;
1082+
res.v_type = VAR_NUMBER;
1083+
1084+
old_lnum = wp->w_cursor.lnum;
1085+
if ((c == 'k' || c == 'K' || c == K_UP) && wp->w_cursor.lnum > 1)
1086+
--wp->w_cursor.lnum;
1087+
if ((c == 'j' || c == 'J' || c == K_DOWN)
1088+
&& wp->w_cursor.lnum < wp->w_buffer->b_ml.ml_line_count)
1089+
++wp->w_cursor.lnum;
1090+
if (old_lnum != wp->w_cursor.lnum)
1091+
{
1092+
popup_highlight_curline(wp);
1093+
return;
1094+
}
1095+
1096+
if (c == 'x' || c == 'X' || c == ESC || c == Ctrl_C)
1097+
{
1098+
// Cancelled, invoke callback with -1
1099+
res.vval.v_number = -1;
1100+
popup_close_and_callback(wp, &res);
1101+
return;
1102+
}
1103+
if (c == ' ' || c == K_KENTER || c == CAR || c == NL)
1104+
{
1105+
// Invoke callback with current index.
1106+
res.vval.v_number = wp->w_cursor.lnum;
1107+
popup_close_and_callback(wp, &res);
1108+
return;
1109+
}
1110+
1111+
filter_handle_drag(wp, c, rettv);
1112+
}
1113+
10021114
/*
10031115
* popup_filter_yesno({text}, {options})
10041116
*/
@@ -1009,36 +1121,26 @@ f_popup_filter_yesno(typval_T *argvars, typval_T *rettv)
10091121
win_T *wp = win_id2wp(id);
10101122
char_u *key = tv_get_string(&argvars[1]);
10111123
typval_T res;
1124+
int c;
10121125

10131126
// If the popup has been closed don't consume the key.
10141127
if (wp == NULL)
10151128
return;
10161129

1130+
c = *key;
1131+
if (c == K_SPECIAL && key[1] != NUL)
1132+
c = TO_SPECIAL(key[1], key[2]);
1133+
10171134
// consume all keys until done
10181135
rettv->vval.v_number = 1;
10191136

1020-
if (STRCMP(key, "y") == 0 || STRCMP(key, "Y") == 0)
1137+
if (c == 'y' || c == 'Y')
10211138
res.vval.v_number = 1;
1022-
else if (STRCMP(key, "n") == 0 || STRCMP(key, "N") == 0
1023-
|| STRCMP(key, "x") == 0 || STRCMP(key, "X") == 0
1024-
|| STRCMP(key, "\x1b") == 0)
1139+
else if (c == 'n' || c == 'N' || c == 'x' || c == 'X' || c == ESC)
10251140
res.vval.v_number = 0;
10261141
else
10271142
{
1028-
int c = *key;
1029-
int row = mouse_row;
1030-
int col = mouse_col;
1031-
1032-
if (c == K_SPECIAL && key[1] != NUL)
1033-
c = TO_SPECIAL(key[1], key[2]);
1034-
if (wp->w_popup_drag
1035-
&& is_mouse_key(c)
1036-
&& (wp == popup_dragwin
1037-
|| wp == mouse_find_win(&row, &col, FIND_POPUP)))
1038-
// allow for dragging the popup
1039-
rettv->vval.v_number = 0;
1040-
1041-
// ignore this key
1143+
filter_handle_drag(wp, c, rettv);
10421144
return;
10431145
}
10441146

@@ -1056,6 +1158,18 @@ f_popup_dialog(typval_T *argvars, typval_T *rettv)
10561158
popup_create(argvars, rettv, TYPE_DIALOG);
10571159
}
10581160

1161+
/*
1162+
* popup_menu({text}, {options})
1163+
*/
1164+
void
1165+
f_popup_menu(typval_T *argvars, typval_T *rettv)
1166+
{
1167+
win_T *wp = popup_create(argvars, rettv, TYPE_MENU);
1168+
1169+
if (wp != NULL)
1170+
popup_highlight_curline(wp);
1171+
}
1172+
10591173
/*
10601174
* popup_notification({text}, {options})
10611175
*/

src/proto/popupwin.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ void popup_adjust_position(win_T *wp);
88
void f_popup_clear(typval_T *argvars, typval_T *rettv);
99
void f_popup_create(typval_T *argvars, typval_T *rettv);
1010
void f_popup_atcursor(typval_T *argvars, typval_T *rettv);
11+
void f_popup_filter_menu(typval_T *argvars, typval_T *rettv);
1112
void f_popup_filter_yesno(typval_T *argvars, typval_T *rettv);
1213
void f_popup_dialog(typval_T *argvars, typval_T *rettv);
14+
void f_popup_menu(typval_T *argvars, typval_T *rettv);
1315
void f_popup_notification(typval_T *argvars, typval_T *rettv);
1416
void f_popup_close(typval_T *argvars, typval_T *rettv);
1517
void f_popup_hide(typval_T *argvars, typval_T *rettv);

0 commit comments

Comments
 (0)