@@ -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 *
755764popup_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 */
0 commit comments