]> granicus.if.org Git - vim/commitdiff
patch 8.1.1609: the user cannot easily close a popup window v8.1.1609
authorBram Moolenaar <Bram@vim.org>
Sun, 30 Jun 2019 16:07:00 +0000 (18:07 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 30 Jun 2019 16:07:00 +0000 (18:07 +0200)
Problem:    The user cannot easily close a popup window.
Solution:   Add the "close" property. (mostly by Masato Nishihata,
            closes #4601)

runtime/doc/popup.txt
src/popupwin.c
src/proto/popupwin.pro
src/structs.h
src/testdir/dumps/Test_popupwin_close_01.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_close_02.dump [new file with mode: 0644]
src/testdir/dumps/Test_popupwin_close_03.dump [new file with mode: 0644]
src/testdir/test_popupwin.vim
src/ui.c
src/version.c

index 8bcad20b460671637fafe224e9515dfa806438cc..99af0a52eb707db77e1f8c1df774030db013f5cd 100644 (file)
@@ -1,4 +1,4 @@
-*popup.txt*  For Vim version 8.1.  Last change: 2019 Jun 29
+*popup.txt*  For Vim version 8.1.  Last change: 2019 Jun 30
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -68,12 +68,12 @@ increase the width or use the "minwidth" property.
 
 By default the 'wrap' option is set, so that no text disappears.  Otherwise,
 if there is not enough space then the window is shifted left in order to
-display more text. This can be disabled with the "fixed" property. Also
-disabled when right-aligned.
+display more text.  When right-aligned the window is shifted right to display
+more text.  The shifting can be disabled with the "fixed" property.
 
 Vim tries to show the popup in the location you specify.  In some cases, e.g.
 when the popup would go outside of the Vim window, it will show it somewhere
-else.  E.g. if you use `popup_atcursor()` the popup normally shows just above
+nearby.  E.g. if you use `popup_atcursor()` the popup normally shows just above
 the current cursor position, but if the cursor is close to the top of the Vim
 window it will be placed below the cursor position.
 
@@ -85,6 +85,18 @@ That way you can still see where it is, even though you cannot see the text
 that it is in.
 
 
+CLOSING THE POPUP WINDOW                       *popup-close*
+
+Normally the plugin that created the popup window is also in charge of closing
+it.  If somehow a popup hangs around, you can close all of them with: >
+       call popup_clear()
+Some popups, such as notifications, close after a specified time.  This can be
+set with the "time" property on `popup_create()`.
+Otherwise, a popup can be closed by clicking on the X in the top-right corner
+or by clicking anywhere inside the popup.  This must be enabled with the
+"close" property.  It is set by default for notifications.
+
+
 TODO:
 - Currently 'buftype' is set to "popup", but all the specifics are on the
   window.  Can we use a "normal" buffer and put the type on the window? (#4595)
@@ -350,13 +362,14 @@ popup_notification({text}, {options})                      *popup_notification()*
                                \ 'drag': 1,
                                \ 'highlight': 'WarningMsg',
                                \ 'border': [],
+                               \ 'close': 'click',
                                \ 'padding': [0,1,0,1],
                                \ })
 <              The PopupNotification highlight group is used instead of
                WarningMsg if it is defined.
 
-               This popup should only be used with the |+timers| feature,
-               otherwise it will not disappear.
+               Without the |+timers| feature the poup will not disappear
+               automatically, the user has to click in it.
 
                The position will be adjusted to avoid overlap with other
                notifications.
@@ -371,25 +384,26 @@ popup_show({id})                                          *popup_show()*
 popup_setoptions({id}, {options})                      *popup_setoptions()*
                Override options in popup {id} with entries in {options}.
                These options can be set:
-                       flip
-                       firstline
-                       title
-                       wrap
+                       border
+                       borderchars
+                       borderhighlight
+                       callback
+                       close
                        drag
+                       filter
+                       firstline
+                       flip
                        highlight
+                       mask
+                       moved
                        padding
-                       border
-                       borderhighlight
-                       borderchars
                        scrollbar
                        scrollbarhighlight
                        thumbhighlight
-                       zindex
-                       mask
                        time
-                       moved
-                       filter
-                       callback
+                       title
+                       wrap
+                       zindex
                The options from |popup_move()| can also be used.
                For "hidden" use |popup_hide()| and |popup_show()|.
                "tabpage" cannot be changed.
@@ -509,6 +523,14 @@ The second argument of |popup_create()| is a dictionary with options:
                        popup does not have a border. As soon as dragging
                        starts and "pos" is "center" it is changed to
                        "topleft".
+       close           When "button" an X is displayed in the top-right, on
+                       top of any border, padding or text.  When clicked on
+                       the X the popup will close.  Any callback is invoked
+                       with the value -2.
+                       When "click" any mouse click in the popup will close
+                       it.
+                       When "none" (the default) mouse clicks do not close
+                       the popup window.
        highlight       Highlight group name to use for the text, stored in
                        the 'wincolor' option.
        padding         List with numbers, defining the padding
index 96263bca4f6eaeacf927e4e52c8510fbf671bfd2..a10d4e0a2f61611d981a316ffb6726a4ebb50871 100644 (file)
@@ -180,6 +180,17 @@ popup_on_border(win_T *wp, int row, int col)
            || (col == popup_width(wp) - 1 && wp->w_popup_border[1] > 0);
 }
 
+/*
+ * Return TRUE if "row"/"col" is on the "X" button of the popup.
+ * The values are relative to the top-left corner.
+ * Caller should check w_popup_close is POPCLOSE_BUTTON.
+ */
+    int
+popup_on_X_button(win_T *wp, int row, int col)
+{
+    return row == 0 && col == popup_width(wp) - 1;
+}
+
 // Values set when dragging a popup window starts.
 static int drag_start_row;
 static int drag_start_col;
@@ -384,6 +395,30 @@ apply_general_options(win_T *wp, dict_T *dict)
     if (di != NULL)
        wp->w_popup_drag = dict_get_number(dict, (char_u *)"drag");
 
+    di = dict_find(dict, (char_u *)"close", -1);
+    if (di != NULL)
+    {
+       int ok = TRUE;
+
+       if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL)
+       {
+           char_u  *s = di->di_tv.vval.v_string;
+
+           if (STRCMP(s, "none") == 0)
+               wp->w_popup_close = POPCLOSE_NONE;
+           else if (STRCMP(s, "button") == 0)
+               wp->w_popup_close = POPCLOSE_BUTTON;
+           else if (STRCMP(s, "click") == 0)
+               wp->w_popup_close = POPCLOSE_CLICK;
+           else
+               ok = FALSE;
+       }
+       else
+           ok = FALSE;
+       if (!ok)
+           semsg(_(e_invargNval), "close", tv_get_string(&di->di_tv));
+    }
+
     str = dict_get_string(dict, (char_u *)"highlight", FALSE);
     if (str != NULL)
        set_string_option_direct_in_win(wp, (char_u *)"wincolor", -1,
@@ -1072,6 +1107,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
 
     // set default values
     wp->w_zindex = POPUPWIN_DEFAULT_ZINDEX;
+    wp->w_popup_close = POPCLOSE_NONE;
 
     if (type == TYPE_NOTIFICATION)
     {
@@ -1106,6 +1142,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
        wp->w_zindex = POPUPWIN_NOTIFICATION_ZINDEX;
        wp->w_minwidth = 20;
        wp->w_popup_drag = 1;
+       wp->w_popup_close = POPCLOSE_CLICK;
        for (i = 0; i < 4; ++i)
            wp->w_popup_border[i] = 1;
        wp->w_popup_padding[1] = 1;
@@ -1241,6 +1278,19 @@ popup_close_and_callback(win_T *wp, typval_T *arg)
     popup_close(id);
 }
 
+/*
+ * Close popup "wp" because of a mouse click.
+ */
+    void
+popup_close_for_mouse_click(win_T *wp)
+{
+    typval_T res;
+
+    res.v_type = VAR_NUMBER;
+    res.vval.v_number = -2;
+    popup_close_and_callback(wp, &res);
+}
+
 /*
  * In a filter: check if the typed key is a mouse event that is used for
  * dragging the popup.
@@ -1816,6 +1866,10 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv)
                break;
            }
 
+       dict_add_string(dict, "close", (char_u *)(
+                   wp->w_popup_close == POPCLOSE_BUTTON ? "button"
+                   : wp->w_popup_close == POPCLOSE_CLICK ? "click" : "none"));
+
 # if defined(FEAT_TIMERS)
        dict_add_number(dict, "time", wp->w_popup_timer != NULL
                                 ?  (long)wp->w_popup_timer->tr_interval : 0L);
@@ -2434,6 +2488,14 @@ update_popups(void (*win_update)(win_T *wp))
            }
        }
 
+       if (wp->w_popup_close == POPCLOSE_BUTTON)
+       {
+           // close button goes on top of anything at the top-right corner
+           buf[mb_char2bytes('X', buf)] = NUL;
+           screen_puts(buf, wp->w_winrow, wp->w_wincol + total_width - 1,
+                     wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
+       }
+
        update_popup_transparent(wp, 0);
 
        // Back to the normal zindex.
index 7ac8612cd7725292fb19c1c394320b9e92181f5c..a251f9ad567b69c3125965be9933ece8faf268f0 100644 (file)
@@ -1,5 +1,6 @@
 /* popupwin.c */
 int popup_on_border(win_T *wp, int row, int col);
+int popup_on_X_button(win_T *wp, int row, int col);
 void popup_start_drag(win_T *wp);
 void popup_drag(win_T *wp);
 void popup_set_firstline(win_T *wp);
@@ -10,6 +11,7 @@ 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 popup_close_for_mouse_click(win_T *wp);
 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);
index 208a11ece610a0c555a594dc8713e5d1cc9385a8..b579de3f5cfbd6ccf78d720257b891b383eeed53 100644 (file)
@@ -1996,6 +1996,12 @@ typedef enum {
     POPPOS_CENTER
 } poppos_T;
 
+typedef enum {
+    POPCLOSE_NONE,
+    POPCLOSE_BUTTON,
+    POPCLOSE_CLICK
+} popclose_T;
+
 # define POPUPWIN_DEFAULT_ZINDEX        50
 # define POPUPMENU_ZINDEX              100
 # define POPUPWIN_DIALOG_ZINDEX                200
@@ -2920,6 +2926,7 @@ struct window_S
     colnr_T    w_popup_mincol;     // close popup if cursor before this col
     colnr_T    w_popup_maxcol;     // close popup if cursor after this col
     int                w_popup_drag;       // allow moving the popup with the mouse
+    popclose_T w_popup_close;      // allow closing the popup with the mouse
     list_T     *w_popup_mask;      // list of lists for "mask"
 
 # if defined(FEAT_TIMERS)
diff --git a/src/testdir/dumps/Test_popupwin_close_01.dump b/src/testdir/dumps/Test_popupwin_close_01.dump
new file mode 100644 (file)
index 0000000..e11ae83
--- /dev/null
@@ -0,0 +1,10 @@
+>╔+0#0000001#ffd7ff255|═@5|X| +0#0000000#ffffff0@66
+|║+0#0000001#ffd7ff255|f|o@1|b|a|r|║| +0#0000000#ffffff0@66
+|╚+0#0000001#ffd7ff255|═@5|╝| +0#0000000#ffffff0@5|n+0#0000001#ffd7ff255|o|t|i|f|i|c|a|t|i|o|n| +0#0000000#ffffff0@48
+|4| @73
+|5| |n+0#0000001#ffd7ff255|o| |b|o|r|d|e|r| |h|e|r|X| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255@12|X| +0#0000000#ffffff0@38
+|6| @20| +0#0000001#ffd7ff255|o|n|l|y| |p|a|d@1|i|n|g| | +0#0000000#ffffff0@38
+|7| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@38
+|8| @73
+|9| @73
+@57|1|,|1| @10|T|o|p| 
diff --git a/src/testdir/dumps/Test_popupwin_close_02.dump b/src/testdir/dumps/Test_popupwin_close_02.dump
new file mode 100644 (file)
index 0000000..75342ef
--- /dev/null
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @12|n+0#0000001#ffd7ff255|o|t|i|f|i|c|a|t|i|o|n| +0#0000000#ffffff0@48
+|4| @73
+|5| |n+0#0000001#ffd7ff255|o| |b|o|r|d|e|r| |h|e|r|X| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255@12|X| +0#0000000#ffffff0@38
+|6| @20| +0#0000001#ffd7ff255|o|n|l|y| |p|a|d@1|i|n|g| | +0#0000000#ffffff0@38
+|7| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@38
+|8| @73
+|9| @73
+|:|c|a|l@1| |C|l|o|s|e|W|i|t|h|X|(|)| @38|1|,|1| @10|T|o|p| 
diff --git a/src/testdir/dumps/Test_popupwin_close_03.dump b/src/testdir/dumps/Test_popupwin_close_03.dump
new file mode 100644 (file)
index 0000000..82438c8
--- /dev/null
@@ -0,0 +1,10 @@
+>1+0&#ffffff0| @73
+|2| @73
+|3| @73
+|4| @73
+|5| |n+0#0000001#ffd7ff255|o| |b|o|r|d|e|r| |h|e|r|X| +0#0000000#ffffff0@5| +0#0000001#ffd7ff255@12|X| +0#0000000#ffffff0@38
+|6| @20| +0#0000001#ffd7ff255|o|n|l|y| |p|a|d@1|i|n|g| | +0#0000000#ffffff0@38
+|7| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@38
+|8| @73
+|9| @73
+|P|o|p|u|p| |c|l|o|s|e|d| |w|i|t|h| |-|2| @36|1|,|1| @10|T|o|p| 
index f4bc33cb1196c4be1c27284494fce7cd22460bfe..a3e6acc5c3a2fec01da676bb108566414af21891 100644 (file)
@@ -365,6 +365,63 @@ func Test_popup_drag()
   call delete('XtestPopupDrag')
 endfunc
 
+func Test_popup_close_with_mouse()
+  if !CanRunVimInTerminal()
+    throw 'Skipped: cannot make screendumps'
+  endif
+  let lines =<< trim END
+       call setline(1, range(1, 20))
+       " With border, can click on X
+       let winid = popup_create('foobar', {
+             \ 'close': 'button',
+             \ 'border': [],
+             \ 'line': 1,
+             \ 'col': 1,
+             \ })
+       func CloseMsg(id, result)
+         echomsg 'Popup closed with ' .. a:result
+       endfunc
+       let winid = popup_create('notification', {
+             \ 'close': 'click',
+             \ 'line': 3,
+             \ 'col': 15,
+             \ 'callback': 'CloseMsg',
+             \ })
+       let winid = popup_create('no border here', {
+             \ 'close': 'button',
+             \ 'line': 5,
+             \ 'col': 3,
+             \ })
+       let winid = popup_create('only padding', {
+             \ 'close': 'button',
+             \ 'padding': [],
+             \ 'line': 5,
+             \ 'col': 23,
+             \ })
+       func CloseWithX()
+         call feedkeys("\<F3>\<LeftMouse>\<LeftRelease>", "xt")
+       endfunc
+       map <silent> <F3> :call test_setmouse(1, len('foobar') + 2)<CR>
+       func CloseWithClick()
+         call feedkeys("\<F4>\<LeftMouse>\<LeftRelease>", "xt")
+       endfunc
+       map <silent> <F4> :call test_setmouse(3, 17)<CR>
+  END
+  call writefile(lines, 'XtestPopupClose')
+  let buf = RunVimInTerminal('-S XtestPopupClose', {'rows': 10})
+  call VerifyScreenDump(buf, 'Test_popupwin_close_01', {})
+
+  call term_sendkeys(buf, ":call CloseWithX()\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_close_02', {})
+
+  call term_sendkeys(buf, ":call CloseWithClick()\<CR>")
+  call VerifyScreenDump(buf, 'Test_popupwin_close_03', {})
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('XtestPopupClose')
+endfunction
+
 func Test_popup_with_mask()
   if !CanRunVimInTerminal()
     throw 'Skipped: cannot make screendumps'
index d09e0ab90d99a748c97f4121b389e1620ae0faf8..1732fe8ff7d92001687256d2e90a47275b41a648 100644 (file)
--- a/src/ui.c
+++ b/src/ui.c
@@ -2929,6 +2929,7 @@ jump_to_mouse(
 #endif
 #ifdef FEAT_TEXT_PROP
     static int   in_popup_win = FALSE;
+    static win_T *click_in_popup_win = NULL;
 #endif
     static int prev_row = -1;
     static int prev_col = -1;
@@ -2957,7 +2958,11 @@ jump_to_mouse(
        dragwin = NULL;
        did_drag = FALSE;
 #ifdef FEAT_TEXT_PROP
+       if (click_in_popup_win != NULL && popup_dragwin == NULL)
+           popup_close_for_mouse_click(click_in_popup_win);
+
        popup_dragwin = NULL;
+       click_in_popup_win = NULL;
 #endif
     }
 
@@ -3001,6 +3006,7 @@ retnomove:
        // Continue a modeless selection in a popup window or dragging it.
        if (in_popup_win)
        {
+           click_in_popup_win = NULL;  // don't close it on release
            if (popup_dragwin != NULL)
            {
                // dragging a popup window
@@ -3050,13 +3056,27 @@ retnomove:
        {
            on_sep_line = 0;
            in_popup_win = TRUE;
-           if (wp->w_popup_drag && popup_on_border(wp, row, col))
+           if (wp->w_popup_close == POPCLOSE_BUTTON
+                   && which_button == MOUSE_LEFT
+                   && popup_on_X_button(wp, row, col))
+           {
+               popup_close_for_mouse_click(wp);
+               return IN_UNKNOWN;
+           }
+           else if (wp->w_popup_drag && popup_on_border(wp, row, col))
            {
                popup_dragwin = wp;
                popup_start_drag(wp);
                return IN_UNKNOWN;
            }
-           if (which_button == MOUSE_LEFT)
+           // Only close on release, otherwise it's not possible to drag or do
+           // modeless selection.
+           else if (wp->w_popup_close == POPCLOSE_CLICK
+                   && which_button == MOUSE_LEFT)
+           {
+               click_in_popup_win = wp;
+           }
+           else if (which_button == MOUSE_LEFT)
                // If the click is in the scrollbar, may scroll up/down.
                popup_handle_scrollbar_click(wp, row, col);
 # ifdef FEAT_CLIPBOARD
@@ -3244,6 +3264,7 @@ retnomove:
                return IN_UNKNOWN;
            }
            // continue a modeless selection in a popup window
+           click_in_popup_win = NULL;
            return IN_OTHER_WIN;
        }
 #endif
index 656cb05101b32290a0be732b6502505d62038f80..89b7f4f42913f90d0331e8bde6dd2c35536f7dd5 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1609,
 /**/
     1608,
 /**/