# HG changeset patch # User Bram Moolenaar # Date 1573681503 -3600 # Node ID fa995154904ec3f56a658fdbaf885f5a2740eb10 # Parent 29a5273d027008aafee43e133593b5f716b1c2c5 patch 8.1.2300: redraw breaks going through list of popup windows Commit: https://github.com/vim/vim/commit/afe45b68a67769a61b44a96b0ffe3bfce4e9316e Author: Bram Moolenaar Date: Wed Nov 13 22:35:19 2019 +0100 patch 8.1.2300: redraw breaks going through list of popup windows Problem: Redraw breaks going through list of popup windows. Solution: Use different flags for popup_reset_handled(). (closes https://github.com/vim/vim/issues/5216) diff --git a/src/mouse.c b/src/mouse.c --- a/src/mouse.c +++ b/src/mouse.c @@ -2921,8 +2921,8 @@ mouse_find_win(int *rowp, int *colp, mou if (popup != IGNORE_POPUP) { - popup_reset_handled(); - while ((wp = find_next_popup(TRUE)) != NULL) + popup_reset_handled(POPUP_HANDLED_1); + while ((wp = find_next_popup(TRUE, POPUP_HANDLED_1)) != NULL) { if (*rowp >= wp->w_winrow && *rowp < wp->w_winrow + popup_height(wp) && *colp >= wp->w_wincol diff --git a/src/popupwin.c b/src/popupwin.c --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2815,28 +2815,30 @@ error_if_popup_window() } /* - * Reset all the POPF_HANDLED flags in global popup windows and popup windows + * Reset all the "handled_flag" flags in global popup windows and popup windows * in the current tab page. + * Each calling function should use a different flag, see the list at + * POPUP_HANDLED_1. This won't work with recursive calls though. */ void -popup_reset_handled() +popup_reset_handled(int handled_flag) { win_T *wp; for (wp = first_popupwin; wp != NULL; wp = wp->w_next) - wp->w_popup_flags &= ~POPF_HANDLED; + wp->w_popup_handled &= ~handled_flag; for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) - wp->w_popup_flags &= ~POPF_HANDLED; + wp->w_popup_handled &= ~handled_flag; } /* - * Find the next visible popup where POPF_HANDLED is not set. + * Find the next visible popup where "handled_flag" is not set. * Must have called popup_reset_handled() first. * When "lowest" is TRUE find the popup with the lowest zindex, otherwise the * popup with the highest zindex. */ win_T * -find_next_popup(int lowest) +find_next_popup(int lowest, int handled_flag) { win_T *wp; win_T *found_wp; @@ -2845,24 +2847,26 @@ find_next_popup(int lowest) found_zindex = lowest ? INT_MAX : 0; found_wp = NULL; for (wp = first_popupwin; wp != NULL; wp = wp->w_next) - if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0 + if ((wp->w_popup_handled & handled_flag) == 0 + && (wp->w_popup_flags & POPF_HIDDEN) == 0 && (lowest ? wp->w_zindex < found_zindex - : wp->w_zindex > found_zindex)) + : wp->w_zindex > found_zindex)) { found_zindex = wp->w_zindex; found_wp = wp; } for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) - if ((wp->w_popup_flags & (POPF_HANDLED|POPF_HIDDEN)) == 0 + if ((wp->w_popup_handled & handled_flag) == 0 + && (wp->w_popup_flags & POPF_HIDDEN) == 0 && (lowest ? wp->w_zindex < found_zindex - : wp->w_zindex > found_zindex)) + : wp->w_zindex > found_zindex)) { found_zindex = wp->w_zindex; found_wp = wp; } if (found_wp != NULL) - found_wp->w_popup_flags |= POPF_HANDLED; + found_wp->w_popup_handled |= handled_flag; return found_wp; } @@ -2929,6 +2933,7 @@ invoke_popup_filter(win_T *wp, int c) { set_vim_var_nr(VV_MOUSE_LNUM, 0); set_vim_var_nr(VV_MOUSE_COL, 0); + set_vim_var_nr(VV_MOUSE_WINID, wp->w_id); } vim_free(argv[1].vval.v_string); clear_tv(&rettv); @@ -2963,9 +2968,9 @@ popup_do_filter(int c) res = TRUE; } - popup_reset_handled(); + popup_reset_handled(POPUP_HANDLED_2); state = get_real_state(); - while (!res && (wp = find_next_popup(FALSE)) != NULL) + while (!res && (wp = find_next_popup(FALSE, POPUP_HANDLED_2)) != NULL) if (wp->w_filter_cb.cb_name != NULL && (wp->w_filter_mode & state) != 0) res = invoke_popup_filter(wp, c); @@ -3005,8 +3010,8 @@ popup_check_cursor_pos() { win_T *wp; - popup_reset_handled(); - while ((wp = find_next_popup(TRUE)) != NULL) + popup_reset_handled(POPUP_HANDLED_3); + while ((wp = find_next_popup(TRUE, POPUP_HANDLED_3)) != NULL) if (wp->w_popup_curwin != NULL && (curwin != wp->w_popup_curwin || curwin->w_cursor.lnum != wp->w_popup_lnum @@ -3242,8 +3247,8 @@ may_update_popup_mask(int type) // Find the window with the lowest zindex that hasn't been handled yet, // so that the window with a higher zindex overwrites the value in // popup_mask. - popup_reset_handled(); - while ((wp = find_next_popup(TRUE)) != NULL) + popup_reset_handled(POPUP_HANDLED_4); + while ((wp = find_next_popup(TRUE, POPUP_HANDLED_4)) != NULL) { int width; int height; @@ -3383,8 +3388,8 @@ update_popups(void (*win_update)(win_T * // Find the window with the lowest zindex that hasn't been updated yet, // so that the window with a higher zindex is drawn later, thus goes on // top. - popup_reset_handled(); - while ((wp = find_next_popup(TRUE)) != NULL) + popup_reset_handled(POPUP_HANDLED_5); + while ((wp = find_next_popup(TRUE, POPUP_HANDLED_5)) != NULL) { // This drawing uses the zindex of the popup window, so that it's on // top of the text but doesn't draw when another popup with higher diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro --- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -40,8 +40,8 @@ void f_popup_getpos(typval_T *argvars, t void f_popup_locate(typval_T *argvars, typval_T *rettv); void f_popup_getoptions(typval_T *argvars, typval_T *rettv); int error_if_popup_window(void); -void popup_reset_handled(void); -win_T *find_next_popup(int lowest); +void popup_reset_handled(int handled_flag); +win_T *find_next_popup(int lowest, int handled_flag); int popup_do_filter(int c); int popup_no_mapping(void); void popup_check_cursor_pos(void); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -3015,6 +3015,7 @@ struct window_S pos_save_T w_save_cursor; // backup of cursor pos and topline #ifdef FEAT_TEXT_PROP int w_popup_flags; // POPF_ values + int w_popup_handled; // POPUP_HANDLE[0-9] flags char_u *w_popup_title; poppos_T w_popup_pos; int w_popup_fixed; // do not shift popup to fit on screen diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2949,4 +2949,38 @@ func Test_popupwin_cancel() call assert_equal({}, popup_getpos(win3)) endfunc +func Test_popupwin_filter_redraw() + " Create two popups with a filter that closes the popup when typing "0". + " Both popups should close, even though the redraw also calls + " popup_reset_handled() + + func CloseFilter(winid, key) + if a:key == '0' + call popup_close(a:winid) + redraw + endif + return 0 " pass the key + endfunc + + let id1 = popup_create('first one', #{ + \ line: 1, + \ col: 1, + \ filter: 'CloseFilter', + \ }) + let id2 = popup_create('second one', #{ + \ line: 9, + \ col: 1, + \ filter: 'CloseFilter', + \ }) + call assert_equal(1, popup_getpos(id1).line) + call assert_equal(9, popup_getpos(id2).line) + + call feedkeys('0', 'xt') + call assert_equal({}, popup_getpos(id1)) + call assert_equal({}, popup_getpos(id2)) + + call popup_clear() + delfunc CloseFilter +endfunc + " vim: shiftwidth=2 sts=2 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2300, +/**/ 2299, /**/ 2298, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -624,15 +624,21 @@ extern int (*dyn_libintl_wputenv)(const // Values for w_popup_flags. #define POPF_IS_POPUP 0x01 // this is a popup window #define POPF_HIDDEN 0x02 // popup is not displayed -#define POPF_HANDLED 0x04 // popup was just redrawn or filtered -#define POPF_CURSORLINE 0x08 // popup is highlighting at the cursorline -#define POPF_ON_CMDLINE 0x10 // popup overlaps command line -#define POPF_DRAG 0x20 // popup can be moved by dragging -#define POPF_RESIZE 0x40 // popup can be resized by dragging -#define POPF_MAPPING 0x80 // mapping keys -#define POPF_INFO 0x100 // used for info of popup menu -#define POPF_INFO_MENU 0x200 // align info popup with popup menu -#define POPF_POSINVERT 0x400 // vertical position can be inverted +#define POPF_CURSORLINE 0x04 // popup is highlighting at the cursorline +#define POPF_ON_CMDLINE 0x08 // popup overlaps command line +#define POPF_DRAG 0x10 // popup can be moved by dragging +#define POPF_RESIZE 0x20 // popup can be resized by dragging +#define POPF_MAPPING 0x40 // mapping keys +#define POPF_INFO 0x80 // used for info of popup menu +#define POPF_INFO_MENU 0x100 // align info popup with popup menu +#define POPF_POSINVERT 0x200 // vertical position can be inverted + +// flags used in w_popup_handled +#define POPUP_HANDLED_1 0x01 // used by mouse_find_win() +#define POPUP_HANDLED_2 0x02 // used by popup_do_filter() +#define POPUP_HANDLED_3 0x04 // used by popup_check_cursor_pos() +#define POPUP_HANDLED_4 0x08 // used by may_update_popup_mask() +#define POPUP_HANDLED_5 0x10 // used by update_popups() #ifdef FEAT_TEXT_PROP # define WIN_IS_POPUP(wp) ((wp)->w_popup_flags != 0)