Mercurial > vim
diff src/popupwin.c @ 17863:08f1dd29550e v8.1.1928
patch 8.1.1928: popup windows don't move with the text when making changes
Commit: https://github.com/vim/vim/commit/12034e22dd80cf533ac1c681be521ab299383f63
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Aug 25 22:25:02 2019 +0200
patch 8.1.1928: popup windows don't move with the text when making changes
Problem: Popup windows don't move with the text when making changes.
Solution: Add the 'textprop" property to the popup window options, position
the popup relative to a text property. (closes #4560)
No tests yet.
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 25 Aug 2019 22:30:03 +0200 |
parents | bdddd215bf09 |
children | f13a5c48320b |
line wrap: on
line diff
--- a/src/popupwin.c +++ b/src/popupwin.c @@ -76,41 +76,6 @@ popup_options_one(dict_T *dict, char_u * } static void -get_pos_options(win_T *wp, dict_T *dict) -{ - char_u *str; - int nr; - dictitem_T *di; - - nr = popup_options_one(dict, (char_u *)"line"); - if (nr > 0) - wp->w_wantline = nr; - nr = popup_options_one(dict, (char_u *)"col"); - if (nr > 0) - wp->w_wantcol = nr; - - di = dict_find(dict, (char_u *)"fixed", -1); - if (di != NULL) - wp->w_popup_fixed = dict_get_number(dict, (char_u *)"fixed") != 0; - - str = dict_get_string(dict, (char_u *)"pos", FALSE); - if (str != NULL) - { - for (nr = 0; - nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T)); - ++nr) - if (STRCMP(str, poppos_entries[nr].pp_name) == 0) - { - wp->w_popup_pos = poppos_entries[nr].pp_val; - nr = -1; - break; - } - if (nr != -1) - semsg(_(e_invarg2), str); - } -} - - static void set_padding_border(dict_T *dict, int *array, char *name, int max_val) { dictitem_T *di; @@ -256,7 +221,6 @@ popup_start_drag(win_T *wp, int row, int { drag_start_row = mouse_row; drag_start_col = mouse_col; - // TODO: handle using different corner if (wp->w_wantline == 0) drag_start_wantline = wp->w_winrow + 1; else @@ -430,7 +394,9 @@ popup_add_timeout(win_T *wp, int time) static void apply_move_options(win_T *wp, dict_T *d) { - int nr; + int nr; + char_u *str; + dictitem_T *di; if ((nr = dict_get_number(d, (char_u *)"minwidth")) > 0) wp->w_minwidth = nr; @@ -440,7 +406,64 @@ apply_move_options(win_T *wp, dict_T *d) wp->w_maxwidth = nr; if ((nr = dict_get_number(d, (char_u *)"maxheight")) > 0) wp->w_maxheight = nr; - get_pos_options(wp, d); + + nr = popup_options_one(d, (char_u *)"line"); + if (nr > 0) + wp->w_wantline = nr; + nr = popup_options_one(d, (char_u *)"col"); + if (nr > 0) + wp->w_wantcol = nr; + + di = dict_find(d, (char_u *)"fixed", -1); + if (di != NULL) + wp->w_popup_fixed = dict_get_number(d, (char_u *)"fixed") != 0; + + str = dict_get_string(d, (char_u *)"pos", FALSE); + if (str != NULL) + { + for (nr = 0; + nr < (int)(sizeof(poppos_entries) / sizeof(poppos_entry_T)); + ++nr) + if (STRCMP(str, poppos_entries[nr].pp_name) == 0) + { + wp->w_popup_pos = poppos_entries[nr].pp_val; + nr = -1; + break; + } + if (nr != -1) + semsg(_(e_invarg2), str); + } + + str = dict_get_string(d, (char_u *)"textprop", FALSE); + if (str != NULL) + { + wp->w_popup_prop_type = 0; + if (*str != NUL) + { + nr = find_prop_type_id(str, wp->w_buffer); + if (nr <= 0) + nr = find_prop_type_id(str, NULL); + if (nr <= 0) + semsg(_(e_invarg2), str); + else + { + wp->w_popup_prop_type = nr; + wp->w_popup_prop_win = curwin; + + di = dict_find(d, (char_u *)"textpropwin", -1); + if (di != NULL) + { + wp->w_popup_prop_win = find_win_by_nr_or_id(&di->di_tv); + if (win_valid(wp->w_popup_prop_win)) + wp->w_popup_prop_win = curwin; + } + } + } + } + + di = dict_find(d, (char_u *)"textpropid", -1); + if (di != NULL) + wp->w_popup_prop_id = dict_get_number(d, (char_u *)"textpropid"); } static void @@ -1015,12 +1038,74 @@ popup_adjust_position(win_T *wp) int org_leftcol = wp->w_leftcol; int org_leftoff = wp->w_popup_leftoff; int minwidth; + int wantline = wp->w_wantline; // adjusted for textprop + int wantcol = wp->w_wantcol; // adjusted for textprop wp->w_winrow = 0; wp->w_wincol = 0; wp->w_leftcol = 0; wp->w_popup_leftoff = 0; wp->w_popup_rightoff = 0; + + // If no line was specified default to vertical centering. + if (wantline == 0) + center_vert = TRUE; + + if (wp->w_popup_prop_type > 0 && win_valid(wp->w_popup_prop_win)) + { + win_T *prop_win = wp->w_popup_prop_win; + textprop_T prop; + linenr_T prop_lnum; + pos_T pos; + int screen_row; + int screen_scol; + int screen_ccol; + int screen_ecol; + + // Popup window is positioned relative to a text property. + if (find_visible_prop(prop_win, + wp->w_popup_prop_type, wp->w_popup_prop_id, + &prop, &prop_lnum) == FAIL) + { + // Text property is no longer visible, hide the popup. + // Unhiding the popup is done in check_popup_unhidden(). + if ((wp->w_popup_flags & POPF_HIDDEN) == 0) + { + wp->w_popup_flags |= POPF_HIDDEN; + --wp->w_buffer->b_nwindows; + if (win_valid(wp->w_popup_prop_win)) + redraw_win_later(wp->w_popup_prop_win, SOME_VALID); + } + return; + } + + // Compute the desired position from the position of the text + // property. Use "wantline" and "wantcol" as offsets. + pos.lnum = prop_lnum; + pos.col = prop.tp_col; + if (wp->w_popup_pos == POPPOS_TOPLEFT + || wp->w_popup_pos == POPPOS_BOTLEFT) + pos.col += prop.tp_len - 1; + textpos2screenpos(prop_win, &pos, &screen_row, + &screen_scol, &screen_ccol, &screen_ecol); + + if (wp->w_popup_pos == POPPOS_TOPLEFT + || wp->w_popup_pos == POPPOS_TOPRIGHT) + // below the text + wantline = screen_row + wantline + 1; + else + // above the text + wantline = screen_row + wantline - 1; + center_vert = FALSE; + if (wp->w_popup_pos == POPPOS_TOPLEFT + || wp->w_popup_pos == POPPOS_BOTLEFT) + // right of the text + wantcol = screen_ecol + wantcol; + else + // left of the text + wantcol = screen_scol + wantcol - 1; + } + if (wp->w_popup_pos == POPPOS_CENTER) { // center after computing the size @@ -1029,22 +1114,20 @@ popup_adjust_position(win_T *wp) } else { - if (wp->w_wantline == 0) - center_vert = TRUE; - else if (wp->w_popup_pos == POPPOS_TOPLEFT - || wp->w_popup_pos == POPPOS_TOPRIGHT) + if (wantline != 0 && (wp->w_popup_pos == POPPOS_TOPLEFT + || wp->w_popup_pos == POPPOS_TOPRIGHT)) { - wp->w_winrow = wp->w_wantline - 1; + wp->w_winrow = wantline - 1; if (wp->w_winrow >= Rows) wp->w_winrow = Rows - 1; } - if (wp->w_wantcol == 0) + if (wantcol == 0) center_hor = TRUE; else if (wp->w_popup_pos == POPPOS_TOPLEFT || wp->w_popup_pos == POPPOS_BOTLEFT) { - wp->w_wincol = wp->w_wantcol - 1; + wp->w_wincol = wantcol - 1; if (wp->w_wincol >= Columns - 3) wp->w_wincol = Columns - 3; } @@ -1161,7 +1244,7 @@ popup_adjust_position(win_T *wp) else if (wp->w_popup_pos == POPPOS_BOTRIGHT || wp->w_popup_pos == POPPOS_TOPRIGHT) { - int leftoff = wp->w_wantcol - (wp->w_width + extra_width); + int leftoff = wantcol - (wp->w_width + extra_width); // Right aligned: move to the right if needed. // No truncation, because that would change the height. @@ -1216,15 +1299,25 @@ popup_adjust_position(win_T *wp) else if (wp->w_popup_pos == POPPOS_BOTRIGHT || wp->w_popup_pos == POPPOS_BOTLEFT) { - if ((wp->w_height + extra_height) <= wp->w_wantline) + if ((wp->w_height + extra_height) <= wantline) // bottom aligned: may move down - wp->w_winrow = wp->w_wantline - (wp->w_height + extra_height); + wp->w_winrow = wantline - (wp->w_height + extra_height); else // not enough space, make top aligned - wp->w_winrow = wp->w_wantline + 1; + wp->w_winrow = wantline + 1; } + if (wp->w_winrow >= Rows) + wp->w_winrow = Rows - 1; + else if (wp->w_winrow < 0) + wp->w_winrow = 0; wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer); + if (win_valid(wp->w_popup_prop_win)) + { + wp->w_popup_prop_changedtick = + CHANGEDTICK(wp->w_popup_prop_win->w_buffer); + wp->w_popup_prop_topline = wp->w_popup_prop_win->w_topline; + } // Need to update popup_mask if the position or size changed. // And redraw windows and statuslines that were behind the popup. @@ -1837,17 +1930,23 @@ popup_close_and_callback(win_T *wp, typv popup_close(id); } + static void +popup_close_with_retval(win_T *wp, int retval) +{ + typval_T res; + + res.v_type = VAR_NUMBER; + res.vval.v_number = retval; + popup_close_and_callback(wp, &res); +} + /* * 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); + popup_close_with_retval(wp, -2); } static void @@ -1864,12 +1963,8 @@ check_mouse_moved(win_T *wp, win_T *mous || mouse_col < wp->w_popup_mouse_mincol || mouse_col > wp->w_popup_mouse_maxcol)) { - typval_T res; - - res.v_type = VAR_NUMBER; - res.vval.v_number = -2; // Careful: this makes "wp" invalid. - popup_close_and_callback(wp, &res); + popup_close_with_retval(wp, -2); } } @@ -2458,10 +2553,22 @@ f_popup_getoptions(typval_T *argvars, ty dict_add_number(dict, "scrollbar", wp->w_want_scrollbar); dict_add_number(dict, "zindex", wp->w_zindex); dict_add_number(dict, "fixed", wp->w_popup_fixed); + if (wp->w_popup_prop_type && win_valid(wp->w_popup_prop_win)) + { + proptype_T *pt = text_prop_type_by_id( + wp->w_popup_prop_win->w_buffer, + wp->w_popup_prop_type); + + if (pt != NULL) + dict_add_string(dict, "textprop", pt->pt_name); + dict_add_number(dict, "textpropwin", wp->w_popup_prop_win->w_id); + dict_add_number(dict, "textpropid", wp->w_popup_prop_id); + } dict_add_string(dict, "title", wp->w_popup_title); dict_add_number(dict, "wrap", wp->w_p_wrap); dict_add_number(dict, "drag", (wp->w_popup_flags & POPF_DRAG) != 0); - dict_add_number(dict, "mapping", (wp->w_popup_flags & POPF_MAPPING) != 0); + dict_add_number(dict, "mapping", + (wp->w_popup_flags & POPF_MAPPING) != 0); dict_add_number(dict, "resize", (wp->w_popup_flags & POPF_RESIZE) != 0); dict_add_number(dict, "cursorline", (wp->w_popup_flags & POPF_CURSORLINE) != 0); @@ -2603,9 +2710,7 @@ invoke_popup_filter(win_T *wp, int c) // 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); + popup_close_with_retval(wp, -1); return 1; } @@ -2687,7 +2792,6 @@ popup_no_mapping(void) popup_check_cursor_pos() { win_T *wp; - typval_T tv; popup_reset_handled(); while ((wp = find_next_popup(TRUE)) != NULL) @@ -2696,11 +2800,7 @@ popup_check_cursor_pos() || curwin->w_cursor.lnum != wp->w_popup_lnum || curwin->w_cursor.col < wp->w_popup_mincol || curwin->w_cursor.col > wp->w_popup_maxcol)) - { - tv.v_type = VAR_NUMBER; - tv.vval.v_number = -1; - popup_close_and_callback(wp, &tv); - } + popup_close_with_retval(wp, -1); } /* @@ -2822,6 +2922,51 @@ update_popup_transparent(win_T *wp, int } /* + * Only called when popup window "wp" is hidden: If the window is positioned + * next to a text property, and it is now visible, then unhide the popup. + * We don't check if visible popups become hidden, that is done in + * popup_adjust_position(). + * Return TRUE if the popup became unhidden. + */ + static int +check_popup_unhidden(win_T *wp) +{ + if (wp->w_popup_prop_type > 0 && win_valid(wp->w_popup_prop_win)) + { + textprop_T prop; + linenr_T lnum; + + if (find_visible_prop(wp->w_popup_prop_win, + wp->w_popup_prop_type, wp->w_popup_prop_id, + &prop, &lnum) == OK) + { + wp->w_popup_flags &= ~POPF_HIDDEN; + ++wp->w_buffer->b_nwindows; + wp->w_popup_prop_topline = 0; // force repositioning + return TRUE; + } + } + return FALSE; +} + +/* + * Return TRUE if popup_adjust_position() needs to be called for "wp". + * That is when the buffer in the popup was changed, or the popup is following + * a textprop and the referenced buffer was changed. + */ + static int +popup_need_position_adjust(win_T *wp) +{ + if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer)) + return TRUE; + if (win_valid(wp->w_popup_prop_win)) + return wp->w_popup_prop_changedtick + != CHANGEDTICK(wp->w_popup_prop_win->w_buffer) + || wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline; + return FALSE; +} + +/* * Update "popup_mask" if needed. * Also recomputes the popup size and positions. * Also updates "popup_visible". @@ -2844,18 +2989,22 @@ may_update_popup_mask(int type) popup_mask_refresh = TRUE; redraw_all_popups = TRUE; } + + // Check if any popup window buffer has changed and if any popup connected + // to a text property has become visible. + for (wp = first_popupwin; wp != NULL; wp = wp->w_next) + if (wp->w_popup_flags & POPF_HIDDEN) + popup_mask_refresh |= check_popup_unhidden(wp); + else if (popup_need_position_adjust(wp)) + popup_mask_refresh = TRUE; + for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) + if (wp->w_popup_flags & POPF_HIDDEN) + popup_mask_refresh |= check_popup_unhidden(wp); + else if (popup_need_position_adjust(wp)) + popup_mask_refresh = TRUE; + if (!popup_mask_refresh) - { - // Check if any popup window buffer has changed. - for (wp = first_popupwin; wp != NULL; wp = wp->w_next) - if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer)) - popup_mask_refresh = TRUE; - for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) - if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer)) - popup_mask_refresh = TRUE; - if (!popup_mask_refresh) - return; - } + return; // Need to update the mask, something has changed. popup_mask_refresh = FALSE; @@ -2886,10 +3035,14 @@ may_update_popup_mask(int type) popup_visible = TRUE; - // Recompute the position if the text changed. - if (redraw_all_popups - || wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer)) + // Recompute the position if the text changed. It may make the popup + // hidden if it's attach to a text property that is no longer visible. + if (redraw_all_popups || popup_need_position_adjust(wp)) + { popup_adjust_position(wp); + if (wp->w_popup_flags & POPF_HIDDEN) + continue; + } width = popup_width(wp); height = popup_height(wp); @@ -3411,13 +3564,7 @@ popup_close_preview(void) win_T *wp = popup_find_preview_window(); if (wp != NULL) - { - typval_T res; - - res.v_type = VAR_NUMBER; - res.vval.v_number = -1; - popup_close_and_callback(wp, &res); - } + popup_close_with_retval(wp, -1); } /* @@ -3434,6 +3581,30 @@ popup_hide_info(void) #endif /* + * Close any popup for a text property associated with "win". + * Return TRUE if a popup was closed. + */ + int +popup_win_closed(win_T *win) +{ + win_T *wp; + + for (wp = first_popupwin; wp != NULL; wp = wp->w_next) + if (wp->w_popup_prop_win == win) + { + popup_close_with_retval(wp, -1); + return TRUE; + } + for (wp = curtab->tp_first_popupwin; wp != NULL; wp = wp->w_next) + if (wp->w_popup_prop_win == win) + { + popup_close_with_retval(wp, -1); + return TRUE; + } + return FALSE; +} + +/* * Set the title of the popup window to the file name. */ void