patch 9.2.0319: popup: rendering issues with partially transparent popups
Commit:
https://github.com/vim/vim/commit/c79edc0df9832715eb2537a8d5e9b91ff191a131
Author: Yasuhiro Matsumoto <
matt...@gmail.com>
Date: Tue Apr 7 20:26:17 2026 +0000
patch 9.2.0319: popup: rendering issues with partially transparent popups
Problem: popup: rendering issues with partially transparent popups.
Solution: Redraw the area under the old popup position on move or
resize. Apply the background blend only to the covered half of
a double-width character. (Yasuhiro Matsumoto)
closes: #19881
Signed-off-by: Yasuhiro Matsumoto <
matt...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/popupwin.c b/src/popupwin.c
index 3ab96cb3e..4b69b6189 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -46,6 +46,8 @@ static void may_start_message_win_timer(win_T *wp);
static int popup_on_cmdline = FALSE;
static void popup_adjust_position(win_T *wp);
+static void redraw_under_popup_area(int winrow, int wincol, int height,
+ int width, int leftoff);
/*
* Get option value for "key", which is "line" or "col".
@@ -3107,6 +3109,14 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED)
{
int id;
win_T *wp;
+#ifdef FEAT_PROP_POPUP
+ int old_popup_active;
+#endif
+ int old_winrow;
+ int old_wincol;
+ int old_popup_height;
+ int old_popup_width;
+ int old_popup_leftoff;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
@@ -3118,6 +3128,16 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED)
if (wp == NULL)
return;
+#ifdef FEAT_PROP_POPUP
+ old_popup_active = (wp->w_popup_flags & POPF_OPACITY)
+ && wp->w_popup_blend > 0;
+#endif
+ old_winrow = wp->w_winrow;
+ old_wincol = wp->w_wincol;
+ old_popup_height = popup_height(wp);
+ old_popup_width = popup_width(wp);
+ old_popup_leftoff = wp->w_popup_leftoff;
+
if (check_for_string_or_list_arg(argvars, 1) == FAIL)
return;
@@ -3132,6 +3152,17 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED)
if (must_redraw < UPD_VALID)
must_redraw = UPD_VALID;
popup_adjust_position(wp);
+
+#ifdef FEAT_PROP_POPUP
+ if (old_popup_active
+ && (old_winrow != wp->w_winrow
+ || old_wincol != wp->w_wincol
+ || old_popup_height != popup_height(wp)
+ || old_popup_width != popup_width(wp)
+ || old_popup_leftoff != wp->w_popup_leftoff))
+ redraw_under_popup_area(old_winrow, old_wincol,
+ old_popup_height, old_popup_width, old_popup_leftoff);
+#endif
}
/*
@@ -3316,6 +3347,49 @@ close_all_popups(int force)
return;
}
+/*
+ * Force windows under a popup area to redraw.
+ */
+ static void
+redraw_under_popup_area(int winrow, int wincol, int height, int width, int leftoff)
+{
+ int r;
+
+ for (r = winrow; r < winrow + height && r < screen_Rows; ++r)
+ {
+ int c;
+ win_T *prev_twp = NULL;
+
+ if (r >= cmdline_row)
+ {
+ clear_cmdline = TRUE;
+ continue;
+ }
+
+ for (c = wincol; c < wincol + width - leftoff && c < screen_Columns; ++c)
+ {
+ int line_cp = r;
+ int col_cp = c;
+ win_T *twp;
+
+ twp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP);
+ if (twp != NULL && twp != prev_twp)
+ {
+ prev_twp = twp;
+ if (line_cp < twp->w_height)
+ {
+ linenr_T lnum;
+
+ (void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum, NULL);
+ redrawWinline(twp, lnum);
+ }
+ else if (line_cp == twp->w_height)
+ twp->w_redr_status = TRUE;
+ }
+ }
+ }
+}
+
/*
* popup_move({id}, {options})
*/
@@ -3329,6 +3403,9 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
int old_wincol;
int old_height;
int old_width;
+ int old_popup_height;
+ int old_popup_width;
+ int old_popup_leftoff;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
@@ -3349,6 +3426,9 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
old_wincol = wp->w_wincol;
old_height = wp->w_height;
old_width = wp->w_width;
+ old_popup_height = popup_height(wp);
+ old_popup_width = popup_width(wp);
+ old_popup_leftoff = wp->w_popup_leftoff;
apply_move_options(wp, dict);
@@ -3367,39 +3447,8 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED)
redraw_win_later(wp, UPD_NOT_VALID);
if ((wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0)
- {
- int total_h = old_height + popup_top_extra(wp)
- + wp->w_popup_border[2] + wp->w_popup_padding[2];
- int row;
-
- for (row = old_winrow;
- row < old_winrow + total_h && row < screen_Rows; ++row)
- {
- if (row >= cmdline_row)
- clear_cmdline = TRUE;
- else
- {
- int line_cp = row;
- int col_cp = old_wincol;
- win_T *twp;
-
- twp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP);
- if (twp != NULL)
- {
- if (line_cp >= twp->w_height)
- twp->w_redr_status = TRUE;
- else
- {
- linenr_T lnum;
-
- (void)mouse_comp_pos(twp, &line_cp, &col_cp,
- &lnum, NULL);
- redrawWinline(twp, lnum);
- }
- }
- }
- }
- }
+ redraw_under_popup_area(old_winrow, old_wincol,
+ old_popup_height, old_popup_width, old_popup_leftoff);
}
}
@@ -3415,7 +3464,13 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
linenr_T old_firstline;
#ifdef FEAT_PROP_POPUP
int old_blend;
+ int old_popup_active;
#endif
+ int old_winrow;
+ int old_wincol;
+ int old_popup_height;
+ int old_popup_width;
+ int old_popup_leftoff;
int old_zindex;
int old_popup_flags;
char_u *old_scrollbar_highlight;
@@ -3441,7 +3496,14 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
old_firstline = wp->w_firstline;
#ifdef FEAT_PROP_POPUP
old_blend = wp->w_popup_blend;
+ old_popup_active = (wp->w_popup_flags & POPF_OPACITY)
+ && wp->w_popup_blend > 0;
#endif
+ old_winrow = wp->w_winrow;
+ old_wincol = wp->w_wincol;
+ old_popup_height = popup_height(wp);
+ old_popup_width = popup_width(wp);
+ old_popup_leftoff = wp->w_popup_leftoff;
old_zindex = wp->w_zindex;
old_popup_flags = wp->w_popup_flags;
old_scrollbar_highlight = wp->w_scrollbar_highlight;
@@ -3514,6 +3576,17 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
// popup_adjust_position() only sets popup_mask_refresh when the
// position or size actually changed.
popup_adjust_position(wp);
+
+#ifdef FEAT_PROP_POPUP
+ if (old_popup_active
+ && (old_winrow != wp->w_winrow
+ || old_wincol != wp->w_wincol
+ || old_popup_height != popup_height(wp)
+ || old_popup_width != popup_width(wp)
+ || old_popup_leftoff != wp->w_popup_leftoff))
+ redraw_under_popup_area(old_winrow, old_wincol,
+ old_popup_height, old_popup_width, old_popup_leftoff);
+#endif
}
/*
@@ -4755,10 +4828,15 @@ draw_opacity_padding_cell(
&& utf_char2cells(ScreenLinesUC[base_off]) == 2)
{
// The left half still has the wide char on screen.
- // Clear it to a space.
+ // Clear it to an unblended space: only the right half is
+ // covered by the popup background.
ScreenLines[base_off] = ' ';
ScreenLinesUC[base_off] = 0;
ScreenAttrs[base_off] = saved_screenattrs[base_save_off];
+ popup_set_base_screen_cell(row, base_col,
+ ScreenLines[base_off],
+ ScreenAttrs[base_off],
+ ScreenLinesUC[base_off]);
screen_char(base_off, row, base_col);
// Draw padding in the right half.
@@ -4778,43 +4856,6 @@ draw_opacity_padding_cell(
return;
}
- // screen_line() already cleared the base cell (popup
- // content was a space). Restore the full wide char from
- // saved background so it shows through with opacity.
- if (base_save_off >= 0
- && saved_screenlinesuc != NULL
- && saved_screenlinesuc[base_save_off] != 0
- && utf_char2cells(
- saved_screenlinesuc[base_save_off]) == 2)
- {
- ScreenLines[base_off] =
- saved_screenlines[base_save_off];
- ScreenLinesUC[base_off] =
- saved_screenlinesuc[base_save_off];
- ScreenLines[off] = saved_screenlines[save_off];
- ScreenLinesUC[off] = saved_screenlinesuc[save_off];
- ScreenAttrs[base_off] =
- saved_screenattrs[base_save_off];
- ScreenAttrs[off] = saved_screenattrs[save_off];
-
- int popup_attr_val =
- get_win_attr(screen_opacity_popup);
- int blend = screen_opacity_popup->w_popup_blend;
- ScreenAttrs[base_off] = hl_blend_attr(
- ScreenAttrs[base_off],
- popup_attr_val, blend, TRUE);
- ScreenAttrs[off] = ScreenAttrs[base_off];
- popup_set_base_screen_cell(row, base_col,
- ScreenLines[base_off],
- ScreenAttrs[base_off],
- ScreenLinesUC[base_off]);
- popup_set_base_screen_cell(row, col,
- ScreenLines[off], ScreenAttrs[off],
- ScreenLinesUC[off]);
- screen_char(base_off, row, base_col);
- return;
- }
-
// Draw padding in the right half.
ScreenLines[off] = ' ';
ScreenAttrs[off] = saved_screenattrs[save_off];
diff --git a/src/version.c b/src/version.c
index 37aed30c3..8e20e639c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 319,
/**/
318,
/**/