mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0129: popup: wrong handling of wide-chars and opacity:0
Problem: popup: wrong handling of wide-chars and opacity:0
Solution: Correctly handle opacity 0, correctly handle wide chars
(Yasuhiro Matsumoto)
closes: #19606
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
1f1b86ac60
commit
433bcf3bec
+20
-13
@@ -3120,6 +3120,10 @@ blend_colors(guicolor_T popup_color, guicolor_T bg_color, int blend_val)
|
||||
if (popup_color == INVALCOLOR)
|
||||
return INVALCOLOR;
|
||||
|
||||
// Fully transparent: use underlying color as-is.
|
||||
if (blend_val >= 100)
|
||||
return bg_color;
|
||||
|
||||
r1 = (popup_color >> 16) & 0xFF;
|
||||
g1 = (popup_color >> 8) & 0xFF;
|
||||
b1 = popup_color & 0xFF;
|
||||
@@ -3162,8 +3166,8 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED)
|
||||
// If both attrs are 0, return 0
|
||||
if (char_attr == 0 && popup_attr == 0)
|
||||
return 0;
|
||||
if (blend >= 100)
|
||||
return char_attr; // Fully transparent, show background only
|
||||
if (blend >= 100 && blend_fg)
|
||||
return char_attr; // Fully transparent for both fg and bg
|
||||
|
||||
#ifdef FEAT_GUI
|
||||
if (gui.in_use)
|
||||
@@ -3205,14 +3209,15 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED)
|
||||
// blend_fg=FALSE: use popup foreground
|
||||
new_en.ae_u.gui.fg_color = popup_aep->ae_u.gui.fg_color;
|
||||
}
|
||||
// Blend background color
|
||||
// Blend background color: blend popup bg toward underlying bg
|
||||
if (popup_aep->ae_u.gui.bg_color != INVALCOLOR)
|
||||
{
|
||||
// Always use popup background, fade to black based on blend
|
||||
int r = ((popup_aep->ae_u.gui.bg_color >> 16) & 0xFF) * (100 - blend) / 100;
|
||||
int g = ((popup_aep->ae_u.gui.bg_color >> 8) & 0xFF) * (100 - blend) / 100;
|
||||
int b = (popup_aep->ae_u.gui.bg_color & 0xFF) * (100 - blend) / 100;
|
||||
new_en.ae_u.gui.bg_color = (r << 16) | (g << 8) | b;
|
||||
guicolor_T underlying_bg = INVALCOLOR;
|
||||
if (char_aep != NULL)
|
||||
underlying_bg = char_aep->ae_u.gui.bg_color;
|
||||
new_en.ae_u.gui.bg_color = blend_colors(
|
||||
popup_aep->ae_u.gui.bg_color,
|
||||
underlying_bg, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3269,11 +3274,13 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED)
|
||||
new_en.ae_u.cterm.fg_rgb = popup_aep->ae_u.cterm.fg_rgb;
|
||||
if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
|
||||
{
|
||||
// Always use popup background, fade to black based on blend
|
||||
int r = ((popup_aep->ae_u.cterm.bg_rgb >> 16) & 0xFF) * (100 - blend) / 100;
|
||||
int g = ((popup_aep->ae_u.cterm.bg_rgb >> 8) & 0xFF) * (100 - blend) / 100;
|
||||
int b = (popup_aep->ae_u.cterm.bg_rgb & 0xFF) * (100 - blend) / 100;
|
||||
new_en.ae_u.cterm.bg_rgb = (r << 16) | (g << 8) | b;
|
||||
// Blend popup bg toward underlying bg
|
||||
guicolor_T underlying_bg = INVALCOLOR;
|
||||
if (char_aep != NULL)
|
||||
underlying_bg = char_aep->ae_u.cterm.bg_rgb;
|
||||
new_en.ae_u.cterm.bg_rgb = blend_colors(
|
||||
popup_aep->ae_u.cterm.bg_rgb,
|
||||
underlying_bg, blend);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+52
-16
@@ -785,7 +785,13 @@ apply_general_options(win_T *wp, dict_T *dict)
|
||||
if (di != NULL)
|
||||
{
|
||||
nr = dict_get_number(dict, "opacity");
|
||||
if (nr > 0 && nr < 100)
|
||||
if (nr == 0)
|
||||
{
|
||||
// opacity: 0, fully transparent
|
||||
wp->w_popup_flags |= POPF_OPACITY;
|
||||
wp->w_popup_blend = 100;
|
||||
}
|
||||
else if (nr > 0 && nr < 100)
|
||||
{
|
||||
// opacity: 1-99, partially transparent
|
||||
// Convert to blend (0=opaque, 100=transparent)
|
||||
@@ -4244,6 +4250,38 @@ popup_need_position_adjust(win_T *wp)
|
||||
return wp->w_cursor.lnum != wp->w_popup_last_curline;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force background windows to redraw rows under an opacity popup.
|
||||
*/
|
||||
static void
|
||||
redraw_win_under_opacity_popup(win_T *wp)
|
||||
{
|
||||
int height;
|
||||
int r;
|
||||
|
||||
if (!(wp->w_popup_flags & POPF_OPACITY) || wp->w_popup_blend <= 0
|
||||
|| (wp->w_popup_flags & POPF_HIDDEN))
|
||||
return;
|
||||
|
||||
height = popup_height(wp);
|
||||
for (r = wp->w_winrow;
|
||||
r < wp->w_winrow + height && r < screen_Rows; ++r)
|
||||
{
|
||||
int line_cp = r;
|
||||
int col_cp = wp->w_wincol;
|
||||
win_T *twp;
|
||||
|
||||
twp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP);
|
||||
if (twp != NULL && line_cp < twp->w_height)
|
||||
{
|
||||
linenr_T lnum;
|
||||
|
||||
(void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum, NULL);
|
||||
redrawWinline(twp, lnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update "popup_mask" if needed.
|
||||
* Also recomputes the popup size and positions.
|
||||
@@ -4281,6 +4319,16 @@ may_update_popup_mask(int type)
|
||||
else if (popup_need_position_adjust(wp))
|
||||
popup_mask_refresh = TRUE;
|
||||
|
||||
// Force background windows to redraw rows under opacity popups.
|
||||
// Opacity popups don't participate in popup_mask, so their area
|
||||
// wouldn't normally be redrawn. Without this, ScreenAttrs retains
|
||||
// blended values from the previous cycle, causing blend accumulation.
|
||||
// This must run every cycle, not just when popup_mask_refresh is set.
|
||||
FOR_ALL_POPUPWINS(wp)
|
||||
redraw_win_under_opacity_popup(wp);
|
||||
FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
|
||||
redraw_win_under_opacity_popup(wp);
|
||||
|
||||
if (!popup_mask_refresh)
|
||||
return;
|
||||
|
||||
@@ -5310,21 +5358,9 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
}
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
if (base_screenlines != NULL)
|
||||
{
|
||||
vim_free(base_screenlines);
|
||||
base_screenlines = NULL;
|
||||
}
|
||||
if (base_screenattrs != NULL)
|
||||
{
|
||||
vim_free(base_screenattrs);
|
||||
base_screenattrs = NULL;
|
||||
}
|
||||
if (base_screenlinesuc != NULL)
|
||||
{
|
||||
vim_free(base_screenlinesuc);
|
||||
base_screenlinesuc = NULL;
|
||||
}
|
||||
VIM_CLEAR(base_screenlines);
|
||||
VIM_CLEAR(base_screenattrs);
|
||||
VIM_CLEAR(base_screenlinesuc);
|
||||
base_screen_rows = 0;
|
||||
base_screen_cols = 0;
|
||||
#endif
|
||||
|
||||
+99
-48
@@ -452,6 +452,48 @@ skip_for_popup(int row, int col)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
/*
|
||||
* For a double-wide character at a popup boundary with opacity:0
|
||||
* (blend==100), the two cells may have different underlying attrs.
|
||||
* Pick the one without a background color to prevent color leaking.
|
||||
*/
|
||||
static void
|
||||
resolve_wide_char_opacity_attrs(
|
||||
int row, int col1, int col2,
|
||||
sattr_T *attr1, sattr_T *attr2)
|
||||
{
|
||||
int bg1, bg2;
|
||||
int base1 = 0;
|
||||
int base2 = 0;
|
||||
attrentry_T *ae;
|
||||
|
||||
if (*attr1 == *attr2)
|
||||
return;
|
||||
|
||||
popup_get_base_screen_cell(row, col1, NULL, &base1, NULL);
|
||||
ae = syn_cterm_attr2entry(base1);
|
||||
# ifdef FEAT_TERMGUICOLORS
|
||||
bg1 = (ae != NULL && !COLOR_INVALID(ae->ae_u.cterm.bg_rgb));
|
||||
# else
|
||||
bg1 = (ae != NULL && ae->ae_u.cterm.bg_color != 0);
|
||||
# endif
|
||||
|
||||
popup_get_base_screen_cell(row, col2, NULL, &base2, NULL);
|
||||
ae = syn_cterm_attr2entry(base2);
|
||||
# ifdef FEAT_TERMGUICOLORS
|
||||
bg2 = (ae != NULL && !COLOR_INVALID(ae->ae_u.cterm.bg_rgb));
|
||||
# else
|
||||
bg2 = (ae != NULL && ae->ae_u.cterm.bg_color != 0);
|
||||
# endif
|
||||
|
||||
if (bg1 && !bg2)
|
||||
*attr1 = *attr2;
|
||||
else if (!bg1 && bg2)
|
||||
*attr2 = *attr1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Move one "cooked" screen line to the screen, but only the characters that
|
||||
* have actually changed. Handle insert/delete character.
|
||||
@@ -605,40 +647,7 @@ screen_line(
|
||||
redraw_this = FALSE;
|
||||
// Check if the character is occluded by a popup.
|
||||
if (redraw_this && skip_for_popup(row, col + coloff))
|
||||
{
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// For transparent popup cells, update the background character
|
||||
// so it shows through the popup.
|
||||
if (screen_opacity_popup && screen_opacity_popup->w_popup_blend > 0)
|
||||
{
|
||||
ScreenLines[off_to] = ScreenLines[off_from];
|
||||
ScreenAttrs[off_to] = ScreenAttrs[off_from];
|
||||
if (enc_utf8)
|
||||
{
|
||||
ScreenLinesUC[off_to] = ScreenLinesUC[off_from];
|
||||
if (ScreenLinesUC[off_from] != 0)
|
||||
{
|
||||
for (int i = 0; i < Screen_mco; ++i)
|
||||
ScreenLinesC[i][off_to] = ScreenLinesC[i][off_from];
|
||||
}
|
||||
}
|
||||
if (char_cells == 2)
|
||||
{
|
||||
ScreenLines[off_to + 1] = ScreenLines[off_from + 1];
|
||||
ScreenAttrs[off_to + 1] = ScreenAttrs[off_from];
|
||||
}
|
||||
if (enc_dbcs == DBCS_JPNU)
|
||||
ScreenLines2[off_to] = ScreenLines2[off_from];
|
||||
|
||||
if (enc_dbcs != 0 && char_cells == 2)
|
||||
screen_char_2(off_to, row, col + coloff);
|
||||
else
|
||||
screen_char(off_to, row, col + coloff);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
redraw_this = FALSE;
|
||||
}
|
||||
redraw_this = FALSE;
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// For popup with opacity windows: if drawing a space, show the
|
||||
@@ -689,7 +698,10 @@ screen_line(
|
||||
ScreenLines[off_to] = ' ';
|
||||
if (enc_utf8)
|
||||
ScreenLinesUC[off_to] = 0;
|
||||
ScreenAttrs[off_to] = hl_blend_attr(ScreenAttrs[off_to],
|
||||
int base_attr = ScreenAttrs[off_to];
|
||||
popup_get_base_screen_cell(row, col + coloff,
|
||||
NULL, &base_attr, NULL);
|
||||
ScreenAttrs[off_to] = hl_blend_attr(base_attr,
|
||||
combined, blend, TRUE);
|
||||
screen_char(off_to, row, col + coloff);
|
||||
opacity_blank = TRUE;
|
||||
@@ -715,12 +727,26 @@ screen_line(
|
||||
int popup_attr = get_win_attr(screen_opacity_popup);
|
||||
int combined = hl_combine_attr(popup_attr, char_attr);
|
||||
int blend = screen_opacity_popup->w_popup_blend;
|
||||
ScreenAttrs[off_to] = hl_blend_attr(ScreenAttrs[off_to],
|
||||
combined, blend, TRUE);
|
||||
int base_attr = ScreenAttrs[off_to];
|
||||
popup_get_base_screen_cell(row, col + coloff,
|
||||
NULL, &base_attr, NULL);
|
||||
ScreenAttrs[off_to] = hl_blend_attr(base_attr,
|
||||
combined, blend, TRUE);
|
||||
screen_char(off_to, row, col + coloff);
|
||||
// For wide background character, also update the second cell.
|
||||
// For wide background character, also update the second cell
|
||||
// with its own base attr (it may be outside the popup area).
|
||||
if (bg_char_cells == 2)
|
||||
ScreenAttrs[off_to + 1] = ScreenAttrs[off_to];
|
||||
{
|
||||
int base_attr2 = ScreenAttrs[off_to + 1];
|
||||
popup_get_base_screen_cell(row, col + coloff + 1,
|
||||
NULL, &base_attr2, NULL);
|
||||
ScreenAttrs[off_to + 1] = hl_blend_attr(base_attr2,
|
||||
combined, blend, TRUE);
|
||||
if (blend == 100)
|
||||
resolve_wide_char_opacity_attrs(row,
|
||||
col + coloff, col + coloff + 1,
|
||||
&ScreenAttrs[off_to], &ScreenAttrs[off_to + 1]);
|
||||
}
|
||||
redraw_this = FALSE;
|
||||
}
|
||||
// When a popup space overlaps the second half of a destroyed wide
|
||||
@@ -741,8 +767,11 @@ screen_line(
|
||||
int combined = hl_combine_attr(popup_attr, char_attr);
|
||||
int blend = screen_opacity_popup->w_popup_blend;
|
||||
ScreenLines[off_to] = ' ';
|
||||
ScreenAttrs[off_to] = hl_blend_attr(ScreenAttrs[off_to],
|
||||
combined, blend, TRUE);
|
||||
int base_attr = ScreenAttrs[off_to];
|
||||
popup_get_base_screen_cell(row, col + coloff,
|
||||
NULL, &base_attr, NULL);
|
||||
ScreenAttrs[off_to] = hl_blend_attr(base_attr,
|
||||
combined, blend, TRUE);
|
||||
screen_char(off_to, row, col + coloff);
|
||||
opacity_blank = TRUE;
|
||||
redraw_this = FALSE;
|
||||
@@ -886,9 +915,10 @@ skip_opacity:
|
||||
if (gui.in_use && changed_this)
|
||||
redraw_next = TRUE;
|
||||
#endif
|
||||
|
||||
ScreenAttrs[off_to] = ScreenAttrs[off_from];
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
// For popup with opacity text: blend background with default (0)
|
||||
// For popup with opacity text: blend background with underlying.
|
||||
if (screen_opacity_popup != NULL
|
||||
&& (flags & SLF_POPUP)
|
||||
&& screen_opacity_popup->w_popup_blend > 0)
|
||||
@@ -896,14 +926,35 @@ skip_opacity:
|
||||
int char_attr = ScreenAttrs[off_from];
|
||||
int popup_attr = get_win_attr(screen_opacity_popup);
|
||||
int blend = screen_opacity_popup->w_popup_blend;
|
||||
// Combine popup window color with the character's own
|
||||
// attribute (e.g. syntax highlighting) so that the
|
||||
// character's foreground color is preserved.
|
||||
int combined = hl_combine_attr(popup_attr, char_attr);
|
||||
ScreenAttrs[off_to] = hl_blend_attr(0, combined, blend, FALSE);
|
||||
}
|
||||
#endif
|
||||
int underlying_attr = 0;
|
||||
|
||||
popup_get_base_screen_cell(row, col + coloff,
|
||||
NULL, &underlying_attr, NULL);
|
||||
ScreenAttrs[off_to] = hl_blend_attr(underlying_attr,
|
||||
combined, blend, FALSE);
|
||||
|
||||
// For double-wide characters, the second cell may have a
|
||||
// different underlying attr (e.g. at popup boundary),
|
||||
// so blend it independently.
|
||||
if (char_cells == 2)
|
||||
{
|
||||
int underlying_attr2 = 0;
|
||||
|
||||
popup_get_base_screen_cell(row, col + coloff + 1,
|
||||
NULL, &underlying_attr2, NULL);
|
||||
ScreenAttrs[off_to + 1] = hl_blend_attr(
|
||||
underlying_attr2, combined, blend,
|
||||
FALSE);
|
||||
if (blend == 100)
|
||||
resolve_wide_char_opacity_attrs(row,
|
||||
col + coloff, col + coloff + 1,
|
||||
&ScreenAttrs[off_to],
|
||||
&ScreenAttrs[off_to + 1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
// For simplicity set the attributes of second half of a
|
||||
// double-wide character equal to the first half.
|
||||
if (char_cells == 2)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
>b+0&#ffffff0|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|b|l|u|e|n|p|o|p|u|p|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|r|e|d| |p|o|p|u|p|h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
@57|1|,|1| @10|T|o|p|
|
||||
@@ -0,0 +1,10 @@
|
||||
>b+0&#ffffff0|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|b|l|u|e|n|p|o|p|u|p|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|r|e|d| |p|o|p|u|p|h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54
|
||||
|0| @55|1|,|1| @10|T|o|p|
|
||||
@@ -4835,6 +4835,42 @@ func Test_popup_opacity_100_blocks_background()
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_opacity_zero()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
call setline(1, repeat(['background text here'], 10))
|
||||
hi BluePopup guibg=darkblue guifg=white
|
||||
hi RedPopup guibg=darkred guifg=white
|
||||
|
||||
" Blue popup with opacity=50 (partially transparent)
|
||||
call popup_create('blue popup', {
|
||||
\ 'line': 3, 'col': 5,
|
||||
\ 'highlight': 'BluePopup',
|
||||
\ 'opacity': 50,
|
||||
\ 'zindex': 1,
|
||||
\ })
|
||||
|
||||
" Red popup with opacity=0 (fully transparent), overlapping the blue one
|
||||
let g:pop_id = popup_create('red popup', {
|
||||
\ 'line': 4, 'col': 8,
|
||||
\ 'highlight': 'RedPopup',
|
||||
\ 'opacity': 0,
|
||||
\ 'zindex': 2,
|
||||
\ })
|
||||
END
|
||||
|
||||
call writefile(lines, 'XtestPopupOpacityZero', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupOpacityZero', #{rows: 10})
|
||||
call VerifyScreenDump(buf, 'Test_popupwin_opacity_zero_01', {})
|
||||
|
||||
call TermWait(buf, 50)
|
||||
call term_sendkeys(buf, ":echo popup_getoptions(g:pop_id)['opacity']\<CR>")
|
||||
call VerifyScreenDump(buf, 'Test_popupwin_opacity_zero_02', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_getwininfo_tabnr()
|
||||
tab split
|
||||
let winid1 = popup_create('sup', #{tabpage: 1})
|
||||
|
||||
@@ -734,6 +734,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
129,
|
||||
/**/
|
||||
128,
|
||||
/**/
|
||||
|
||||
Reference in New Issue
Block a user