]> granicus.if.org Git - vim/commitdiff
patch 8.1.1548: popup_dialog() is not implemented v8.1.1548
authorBram Moolenaar <Bram@vim.org>
Sat, 15 Jun 2019 19:46:30 +0000 (21:46 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 15 Jun 2019 19:46:30 +0000 (21:46 +0200)
Problem:    Popup_dialog() is not implemented.
Solution:   Implement popup_dialog() and popup_filter_yesno().

runtime/doc/popup.txt
src/evalfunc.c
src/globals.h
src/popupwin.c
src/proto/popupwin.pro
src/structs.h
src/testdir/test_popupwin.vim
src/version.c

index 3ee92f2698083b53053d5c3de8f0e696ca69bdbe..a9f55be2799c7250d9a5ed0cbc4ffc21fb6cc552 100644 (file)
@@ -103,10 +103,10 @@ TODO:
 - When drawing on top half a double-wide character, display ">" or "<" in the
   incomplete cell.
 - Can the buffer be re-used, to avoid using up lots of buffer numbers?
+- Use a popup window for the "info" item of completion instead of using a
+  preview window.
 - Implement:
-       popup_dialog({text}, {options})
        popup_filter_menu({id}, {key})
-       popup_filter_yesno({id}, {key})
        popup_menu({text}, {options})
        popup_setoptions({id}, {options})
        flip option
@@ -196,16 +196,23 @@ popup_create({text}, {options})                           *popup_create()*
 
 
 popup_dialog({text}, {options})                                *popup_dialog()*
-               {not implemented yet}
                Just like |popup_create()| but with these default options: >
                        call popup_create({text}, {
                                \ 'pos': 'center',
                                \ 'zindex': 200,
+                               \ 'drag': 1,
                                \ 'border': [],
                                \ 'padding': [],
                                \})
 <              Use {options} to change the properties. E.g. add a 'filter'
-               option with value 'popup_filter_yesno'.
+               option with value 'popup_filter_yesno'.  Example: >
+                       call popup_create('do you want to quit (Yes/no)?', {
+                               \ 'filter': 'popup_filter_yesno',
+                               \ 'callback': 'QuitCallback',
+                               \ })
+
+<              By default the dialog can be dragged, so that text below it
+               can be read if needed.
 
 
 popup_filter_menu({id}, {key})                         *popup_filter_menu()*
@@ -218,12 +225,12 @@ popup_filter_menu({id}, {key})                            *popup_filter_menu()*
 
 
 popup_filter_yesno({id}, {key})                                *popup_filter_yesno()*
-               {not implemented yet}
                Filter that can be used for a popup. It handles only the keys
                'y', 'Y' and 'n' or 'N'.  Invokes the "callback" of the
                popup menu with the 1 for 'y' or 'Y' and zero for 'n' or 'N'
-               as the second argument.  Pressing Esc and CTRL-C works like
-               pressing 'n'.  Other keys are ignored.
+               as the second argument.  Pressing Esc and 'x' works like
+               pressing 'n'.  CTRL-C invokes the callback with -1.  Other
+               keys are ignored.
 
 
 popup_getoptions({id})                                 *popup_getoptions()*
@@ -301,7 +308,7 @@ popup_notification({text}, {options})                        *popup_notification()*
                                \ 'minwidth': 20,
                                \ 'time': 3000,
                                \ 'tabpage': -1,
-                               \ 'zindex': 200,
+                               \ 'zindex': 300,
                                \ 'drag': 1,
                                \ 'highlight': 'WarningMsg',
                                \ 'border': [],
@@ -521,7 +528,7 @@ filter is also called.  The filter of the popup window with the highest zindex
 is called first.
 
 The filter function is called with two arguments: the ID of the popup and the
-key, e.g.: >
+key as a string, e.g.: >
        func MyFilter(winid, key)
          if a:key == "\<F2>"
            " do something
@@ -556,15 +563,14 @@ Vim recognizes the Esc key.  If you do use Esc, it is recommended to set the
 
 POPUP CALLBACK                                         *popup-callback*
 
-A callback that is invoked when the popup closes.  Used by
-|popup_filter_menu()|.
+A callback that is invoked when the popup closes.
 
 The callback is invoked with two arguments: the ID of the popup window and the
 result, which could be an index in the popup lines, or whatever was passed as
 the second argument of `popup_close()`.
 
-If the popup is closed because the cursor moved, the number -1 is passed to
-the callback.
+If the popup is force-closed, e.g. because the cursor moved or CTRL-C was
+pressed, the number -1 is passed to the callback.
 
 ==============================================================================
 3. Examples                                            *popup-examples*
index 544426fa6d71c0820af6ac9caa4c17420eaad087..d371c28b664fa220ba78c46b7c0c1700b14e3ac1 100644 (file)
@@ -815,6 +815,8 @@ static struct fst
     {"popup_clear",    0, 0, f_popup_clear},
     {"popup_close",    1, 2, f_popup_close},
     {"popup_create",   2, 2, f_popup_create},
+    {"popup_dialog",   2, 2, f_popup_dialog},
+    {"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},
index 992ce2a54378cb36879c0ad356a6695c1d3d83b2..ec429849447c06fbac4eccfa2c6a7b58612d7f13 100644 (file)
@@ -599,7 +599,8 @@ EXTERN win_T        *aucmd_win;     /* window used in aucmd_prepbuf() */
 EXTERN int     aucmd_win_used INIT(= FALSE);   /* aucmd_win is being used */
 
 #ifdef FEAT_TEXT_PROP
-EXTERN win_T    *first_popupwin;       // first global popup window
+EXTERN win_T    *first_popupwin;               // first global popup window
+EXTERN win_T   *popup_dragwin INIT(= NULL);    // popup window being dragged
 #endif
 
 /*
index aeed2e79b4a68f6fb045bcbad5cd41851dd3681d..adbc72914938b877cd01510a6663fe725893d9d9 100644 (file)
@@ -201,6 +201,10 @@ popup_start_drag(win_T *wp)
        drag_start_wantcol = wp->w_wincol + 1;
     else
        drag_start_wantcol = wp->w_wantcol;
+
+    // Stop centering the popup
+    if (wp->w_popup_pos == POPPOS_CENTER)
+       wp->w_popup_pos = POPPOS_TOPLEFT;
 }
 
 /*
@@ -301,7 +305,9 @@ apply_options(win_T *wp, buf_T *buf UNUSED, dict_T *dict)
        wp->w_p_wrap = nr != 0;
     }
 
-    wp->w_popup_drag = dict_get_number(dict, (char_u *)"drag");
+    di = dict_find(dict, (char_u *)"drag", -1);
+    if (di != NULL)
+       wp->w_popup_drag = dict_get_number(dict, (char_u *)"drag");
 
     di = dict_find(dict, (char_u *)"callback", -1);
     if (di != NULL)
@@ -692,13 +698,13 @@ typedef enum
 {
     TYPE_NORMAL,
     TYPE_ATCURSOR,
-    TYPE_NOTIFICATION
+    TYPE_NOTIFICATION,
+    TYPE_DIALOG
 } create_type_T;
 
 /*
  * popup_create({text}, {options})
  * popup_atcursor({text}, {options})
- * When called from f_popup_atcursor() "type" is TYPE_ATCURSOR.
  */
     static void
 popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
@@ -871,6 +877,20 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
                OPT_FREE|OPT_LOCAL, 0);
     }
 
+    if (type == TYPE_DIALOG)
+    {
+       int i;
+
+       wp->w_popup_pos = POPPOS_CENTER;
+       wp->w_zindex = POPUPWIN_DIALOG_ZINDEX;
+       wp->w_popup_drag = 1;
+       for (i = 0; i < 4; ++i)
+       {
+           wp->w_popup_border[i] = 1;
+           wp->w_popup_padding[i] = 1;
+       }
+    }
+
     // Deal with options.
     apply_options(wp, buf, argvars[1].vval.v_dict);
 
@@ -912,33 +932,6 @@ f_popup_atcursor(typval_T *argvars, typval_T *rettv)
     popup_create(argvars, rettv, TYPE_ATCURSOR);
 }
 
-/*
- * popup_notification({text}, {options})
- */
-    void
-f_popup_notification(typval_T *argvars, typval_T *rettv)
-{
-    popup_create(argvars, rettv, TYPE_NOTIFICATION);
-}
-
-/*
- * Find the popup window with window-ID "id".
- * If the popup window does not exist NULL is returned.
- * If the window is not a popup window, and error message is given.
- */
-    static win_T *
-find_popup_win(int id)
-{
-    win_T *wp = win_id2wp(id);
-
-    if (wp != NULL && !bt_popup(wp->w_buffer))
-    {
-       semsg(_("E993: window %d is not a popup window"), id);
-       return NULL;
-    }
-    return wp;
-}
-
 /*
  * Invoke the close callback for window "wp" with value "result".
  * Careful: The callback may make "wp" invalid!
@@ -985,6 +978,90 @@ popup_close_and_callback(win_T *wp, typval_T *arg)
     popup_close(id);
 }
 
+/*
+ * popup_filter_yesno({text}, {options})
+ */
+    void
+f_popup_filter_yesno(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;
+
+    // If the popup has been closed don't consume the key.
+    if (wp == NULL)
+       return;
+
+    // consume all keys until done
+    rettv->vval.v_number = 1;
+
+    if (STRCMP(key, "y") == 0 || STRCMP(key, "Y") == 0)
+       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)
+       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
+       return;
+    }
+
+    // Invoke callback
+    res.v_type = VAR_NUMBER;
+    popup_close_and_callback(wp, &res);
+}
+
+/*
+ * popup_dialog({text}, {options})
+ */
+    void
+f_popup_dialog(typval_T *argvars, typval_T *rettv)
+{
+    popup_create(argvars, rettv, TYPE_DIALOG);
+}
+
+/*
+ * popup_notification({text}, {options})
+ */
+    void
+f_popup_notification(typval_T *argvars, typval_T *rettv)
+{
+    popup_create(argvars, rettv, TYPE_NOTIFICATION);
+}
+
+/*
+ * Find the popup window with window-ID "id".
+ * If the popup window does not exist NULL is returned.
+ * If the window is not a popup window, and error message is given.
+ */
+    static win_T *
+find_popup_win(int id)
+{
+    win_T *wp = win_id2wp(id);
+
+    if (wp != NULL && !bt_popup(wp->w_buffer))
+    {
+       semsg(_("E993: window %d is not a popup window"), id);
+       return NULL;
+    }
+    return wp;
+}
+
 /*
  * popup_close({id})
  */
@@ -1299,6 +1376,15 @@ invoke_popup_filter(win_T *wp, int c)
     typval_T   argv[3];
     char_u     buf[NUMBUFLEN];
 
+    // Emergency exit: CTRL-C closes the popup.
+    if (c == Ctrl_C)
+    {
+       rettv.v_type = VAR_NUMBER;
+       rettv.vval.v_number = -1;
+       popup_close_and_callback(wp, &rettv);
+       return 1;
+    }
+
     argv[0].v_type = VAR_NUMBER;
     argv[0].vval.v_number = (varnumber_T)wp->w_id;
 
@@ -1310,6 +1396,7 @@ invoke_popup_filter(win_T *wp, int c)
 
     argv[2].v_type = VAR_UNKNOWN;
 
+    // NOTE: The callback might close the popup, thus make "wp" invalid.
     call_callback(&wp->w_filter_cb, -1,
                            &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
     res = tv_get_number(&rettv);
@@ -1326,7 +1413,7 @@ invoke_popup_filter(win_T *wp, int c)
 popup_do_filter(int c)
 {
     int                res = FALSE;
-    win_T   *wp;
+    win_T      *wp;
 
     popup_reset_handled();
 
index cde0cae6e8b29ed3b2d874a22aca5c440b31fa0a..6e93791e61ae4b0b6c25a8caa9b4c5c46b6350ed 100644 (file)
@@ -8,6 +8,8 @@ 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_yesno(typval_T *argvars, typval_T *rettv);
+void f_popup_dialog(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);
index 59259274a19a258caaf78520dcbb0866a96ebbe2..09158a0cc5a903a23d6b3fea19c6eb3ba20f8ad2 100644 (file)
@@ -1998,7 +1998,8 @@ typedef enum {
 
 # define POPUPWIN_DEFAULT_ZINDEX        50
 # define POPUPMENU_ZINDEX              100
-# define POPUPWIN_NOTIFICATION_ZINDEX   200
+# define POPUPWIN_DIALOG_ZINDEX                200
+# define POPUPWIN_NOTIFICATION_ZINDEX   300
 #endif
 
 /*
index a3f2046f4c3fbdc2fa321dde729592f1c90910d6..f7a319c23f1abae5769b7359bafec18643912c45 100644 (file)
@@ -870,6 +870,36 @@ func Test_popup_filter()
   call popup_clear()
 endfunc
 
+func ShowDialog(key, result)
+  let s:cb_res = 999
+  let winid = popup_dialog('do you want to quit (Yes/no)?', {
+         \ 'filter': 'popup_filter_yesno',
+         \ '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_dialog()
+  func QuitCallback(id, res)
+    let s:cb_winid = a:id
+    let s:cb_res = a:res
+  endfunc
+
+  let winid = ShowDialog("y", 1)
+  let winid = ShowDialog("Y", 1)
+  let winid = ShowDialog("n", 0)
+  let winid = ShowDialog("N", 0)
+  let winid = ShowDialog("x", 0)
+  let winid = ShowDialog("X", 0)
+  let winid = ShowDialog("\<Esc>", 0)
+  let winid = ShowDialog("\<C-C>", -1)
+
+  delfunc QuitCallback
+endfunc
+
 func Test_popup_close_callback()
   func PopupDone(id, result)
     let g:result = a:result
index 113fb97ce0b6d564c7d8a66a03ce179e4b3ed3aa..9d457038e1bc035f5760fefd94f73d35a5f1b149 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1548,
 /**/
     1547,
 /**/