patch 9.2.0318: cannot configure opacity for popup menu

Problem:  cannot configure opacity for popup menu
Solution: Add the 'pumopt' option, consolidate existing pum options into
          the pumopt option (Yasuhiro Matsumoto)

closes: #19931

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yasuhiro Matsumoto
2026-04-07 19:51:20 +00:00
committed by Christian Brabandt
parent f3cba4a205
commit 317351c745
22 changed files with 714 additions and 98 deletions
+17
View File
@@ -125,6 +125,23 @@ EXTERN int screen_zindex INIT(= 0);
EXTERN win_T *screen_opacity_popup INIT(= NULL);
#endif
// Pum opacity level (0 = fully transparent, 100 = fully opaque).
// Set via 'pumopt' opacity: key.
EXTERN long p_po INIT(= 100);
// Blend value for popup menu opacity (0 = off, 1-99 = blend level).
// Set during pum drawing when pum opacity is active.
EXTERN int screen_pum_blend INIT(= 0);
// Saved background screen content for pum opacity blending.
EXTERN sattr_T *pum_bg_attrs INIT(= NULL);
EXTERN schar_T *pum_bg_lines INIT(= NULL);
EXTERN u8char_T *pum_bg_linesUC INIT(= NULL);
EXTERN u8char_T *pum_bg_linesC[MAX_MCO];
EXTERN int pum_bg_top INIT(= 0);
EXTERN int pum_bg_bot INIT(= 0);
EXTERN int pum_bg_cols INIT(= 0);
EXTERN int screen_Rows INIT(= 0); // actual size of ScreenLines[]
EXTERN int screen_Columns INIT(= 0); // actual size of ScreenLines[]
+132
View File
@@ -3321,6 +3321,138 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED)
return get_attr_entry(&term_attr_table, &new_en);
}
/*
* Blend for pum opacity space cells: keep underlying fg, blend bg.
* This is different from hl_blend_attr(blend_fg=TRUE) where fg blends
* in the wrong direction for pum use.
*/
int
hl_pum_blend_attr(int char_attr, int popup_attr, int blend UNUSED)
{
attrentry_T *char_aep = NULL;
attrentry_T *popup_aep;
attrentry_T new_en;
#ifdef FEAT_GUI
if (gui.in_use)
{
if (char_attr > HL_ALL)
char_aep = syn_gui_attr2entry(char_attr);
if (char_aep != NULL)
new_en = *char_aep;
else
{
CLEAR_FIELD(new_en);
new_en.ae_u.gui.fg_color = INVALCOLOR;
new_en.ae_u.gui.bg_color = INVALCOLOR;
new_en.ae_u.gui.sp_color = INVALCOLOR;
if (char_attr <= HL_ALL)
new_en.ae_attr = char_attr;
}
if (popup_attr > HL_ALL)
{
popup_aep = syn_gui_attr2entry(popup_attr);
if (popup_aep != NULL)
{
// Blend fg: pum_bg toward underlying_fg.
// blend=0 (opaque): fg = pum_bg (text hidden)
// blend=100 (transparent): fg = underlying_fg (text visible)
if (popup_aep->ae_u.gui.bg_color != INVALCOLOR)
{
int base_fg = 0xFFFFFF;
if (char_aep != NULL
&& char_aep->ae_u.gui.fg_color != INVALCOLOR)
base_fg = char_aep->ae_u.gui.fg_color;
new_en.ae_u.gui.fg_color = blend_colors(
popup_aep->ae_u.gui.bg_color, base_fg, blend);
}
// Blend bg: popup bg toward underlying bg.
if (popup_aep->ae_u.gui.bg_color != INVALCOLOR)
{
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);
}
}
}
return get_attr_entry(&gui_attr_table, &new_en);
}
#endif
if (IS_CTERM)
{
if (char_attr > HL_ALL)
char_aep = syn_cterm_attr2entry(char_attr);
if (char_aep != NULL)
new_en = *char_aep;
else
{
CLEAR_FIELD(new_en);
#ifdef FEAT_TERMGUICOLORS
new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
#endif
if (char_attr <= HL_ALL)
new_en.ae_attr = char_attr;
}
if (popup_attr > HL_ALL)
{
popup_aep = syn_cterm_attr2entry(popup_attr);
if (popup_aep != NULL)
{
// Blend cterm fg: use popup bg (hides text when opaque)
if (popup_aep->ae_u.cterm.fg_color > 0)
new_en.ae_u.cterm.fg_color =
popup_aep->ae_u.cterm.fg_color;
// Use popup cterm bg.
if (popup_aep->ae_u.cterm.bg_color > 0)
new_en.ae_u.cterm.bg_color =
popup_aep->ae_u.cterm.bg_color;
#ifdef FEAT_TERMGUICOLORS
// Blend fg_rgb: pum_bg toward underlying_fg.
if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
{
int base_fg = 0xFFFFFF;
if (char_aep != NULL
&& char_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
base_fg = char_aep->ae_u.cterm.fg_rgb;
new_en.ae_u.cterm.fg_rgb = blend_colors(
popup_aep->ae_u.cterm.bg_rgb, base_fg, blend);
}
// Blend bg_rgb.
if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
{
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
}
}
return get_attr_entry(&cterm_attr_table, &new_en);
}
// term mode
if (char_attr > HL_ALL)
char_aep = syn_term_attr2entry(char_attr);
if (char_aep != NULL)
new_en = *char_aep;
else
{
CLEAR_FIELD(new_en);
if (char_attr <= HL_ALL)
new_en.ae_attr = char_attr;
}
return get_attr_entry(&term_attr_table, &new_en);
}
#ifdef FEAT_GUI
attrentry_T *
syn_gui_attr2entry(int attr)
+1
View File
@@ -556,6 +556,7 @@ EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
EXTERN long p_pmw; // 'pummaxwidth'
EXTERN char_u *p_pb; // 'pumborder'
EXTERN char_u *p_pumopt; // 'pumopt'
EXTERN char_u *p_com; // 'comments'
EXTERN char_u *p_cpo; // 'cpoptions'
#ifdef FEAT_CSCOPE
+4
View File
@@ -2091,6 +2091,10 @@ static struct vimoption options[] =
{"pummaxwidth", "pmw", P_NUM|P_VI_DEF,
(char_u *)&p_pmw, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L} SCTX_INIT},
{"pumopt", NULL, P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_COLON,
(char_u *)&p_pumopt, PV_NONE,
did_set_pumopt, expand_set_pumopt,
{(char_u *)"", (char_u *)NULL} SCTX_INIT},
{"pumwidth", "pw", P_NUM|P_VI_DEF,
(char_u *)&p_pw, PV_NONE, NULL, NULL,
{(char_u *)15L, (char_u *)15L} SCTX_INIT},
+204 -69
View File
@@ -3696,6 +3696,197 @@ expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matches)
pum_set_margin(FALSE); \
} while (0)
/*
* Parse a border value from a pumopt "border:" token.
* Returns OK on success, FAIL on error.
*/
static int
parse_pumopt_border(char_u *val, int len)
{
// Use box-drawing characters only when 'encoding' is "utf-8" and
// 'ambiwidth' is "single".
int can_use_box_chars = (enc_utf8 && *p_ambw == 's');
char_u *token;
token = vim_strnsave(val, len);
if (token == NULL)
return FAIL;
if (can_use_box_chars && STRCMP(token, "single") == 0)
pum_set_border_chars(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x250c, 0x2510, 0x2518, 0x2514); // ┌ ┐ ┘ └
else if (can_use_box_chars && STRCMP(token, "double") == 0)
pum_set_border_chars(0x2550, 0x2551, 0x2550, 0x2551, // ═ ║ ═ ║
0x2554, 0x2557, 0x255D, 0x255A); // ╔ ╗ ╝ ╚
else if (can_use_box_chars && STRCMP(token, "round") == 0)
pum_set_border_chars(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x256d, 0x256e, 0x256f, 0x2570); // ╭ ╮ ╯ ╰
else if (STRCMP(token, "ascii") == 0)
pum_set_border_chars('-', '|', '-', '|', '+', '+', '+', '+');
else if (STRNCMP(token, "custom:", 7) == 0)
{
char_u *q = token + 7;
int out[8];
for (int i = 0; i < 8; i++)
{
if (*q == NUL || *q == ',')
{
vim_free(token);
return FAIL;
}
out[i] = mb_ptr2char(q);
mb_ptr2char_adv(&q);
if (i < 7)
{
if (*q != ';')
{
vim_free(token);
return FAIL;
}
q++;
}
}
if (*q != NUL && *q != ',')
{
vim_free(token);
return FAIL;
}
pum_set_border_chars(out[0], out[1], out[2], out[3], out[4], out[5],
out[6], out[7]);
}
else
{
vim_free(token);
return FAIL;
}
vim_free(token);
pum_set_border(TRUE);
return OK;
}
/*
* The 'pumopt' option is changed.
* Format: comma-separated key:value pairs.
* border:{single|double|round|ascii|custom:X;X;X;X;X;X;X;X}
* height:{n}
* width:{n}
* maxwidth:{n}
* opacity:{n}
* shadow
* margin (requires border)
*/
char *
did_set_pumopt(optset_T *args)
{
char_u **varp = (char_u **)args->os_varp;
char_u *p;
int have_border = FALSE;
int have_margin = FALSE;
// Reset to defaults.
PUM_BORDER_CLEAR();
p_ph = 0;
p_pw = 15;
p_pmw = 0;
p_po = 100;
if (*varp == NULL || **varp == NUL)
return NULL;
for (p = *varp; p != NULL && *p != NUL; )
{
char_u *comma = vim_strchr(p, ',');
int len;
if (comma != NULL)
len = (int)(comma - p);
else
len = (int)STRLEN(p);
if (STRNCMP(p, "border:", 7) == 0)
{
if (have_border)
goto error;
have_border = TRUE;
if (parse_pumopt_border(p + 7, len - 7) == FAIL)
goto error;
}
else if (STRNCMP(p, "height:", 7) == 0)
{
long n = atol((char *)p + 7);
if (n < 0)
goto error;
p_ph = n;
}
else if (STRNCMP(p, "width:", 6) == 0)
{
long n = atol((char *)p + 6);
if (n < 0)
goto error;
p_pw = n;
}
else if (STRNCMP(p, "maxwidth:", 9) == 0)
{
long n = atol((char *)p + 9);
if (n < 0)
goto error;
p_pmw = n;
}
else if (STRNCMP(p, "opacity:", 8) == 0)
{
long n = atol((char *)p + 8);
if (n < 0 || n > 100)
goto error;
p_po = n;
}
else if (len == 6 && STRNCMP(p, "shadow", 6) == 0)
pum_set_shadow(TRUE);
else if (len == 6 && STRNCMP(p, "margin", 6) == 0)
{
have_margin = TRUE;
pum_set_margin(TRUE);
}
else
goto error;
if (comma != NULL)
p = comma + 1;
else
break;
}
if (have_margin && !have_border)
goto error;
// Invalidate cached background for opacity changes.
pum_opacity_changed();
return NULL;
error:
PUM_BORDER_CLEAR();
p_ph = 0;
p_pw = 15;
p_pmw = 0;
p_po = 100;
return e_invalid_argument;
}
int
expand_set_pumopt(optexpand_T *args, int *numMatches, char_u ***matches)
{
static char *(p_pumopt_values[]) = {"border:", "height:", "width:",
"maxwidth:", "opacity:", "shadow", "margin", NULL};
return expand_set_opt_string(
args,
p_pumopt_values,
ARRAY_LENGTH(p_pumopt_values) - 1,
numMatches,
matches);
}
/*
* The 'pumborder' option is changed.
* Rules:
@@ -3707,10 +3898,7 @@ expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matches)
did_set_pumborder(optset_T *args)
{
char_u **varp = (char_u **)args->os_varp;
// Use box-drawing characters only when 'encoding' is "utf-8" and
// 'ambiwidth' is "single".
int can_use_box_chars = (enc_utf8 && *p_ambw == 's');
char_u *p, *token;
char_u *p;
int len;
int have_border = FALSE;
int have_margin = FALSE;
@@ -3722,89 +3910,36 @@ did_set_pumborder(optset_T *args)
for (p = *varp; p != NULL && *p != NUL; )
{
// end of token is either ',' or NUL
char_u *comma = vim_strchr(p, ',');
if (comma != NULL)
len = (int)(comma - p);
else
len = (int)STRLEN(p);
token = vim_strnsave(p, len);
if (token == NULL)
goto error;
if ((can_use_box_chars && (STRCMP(token, "single") == 0
|| STRCMP(token, "double") == 0
|| STRCMP(token, "round") == 0))
|| STRCMP(token, "ascii") == 0
|| (STRNCMP(token, "custom:", 7) == 0))
{
if (have_border)
{
// multiple border styles not allowed
vim_free(token);
goto error;
}
have_border = TRUE;
if (STRCMP(token, "single") == 0)
pum_set_border_chars(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x250c, 0x2510, 0x2518, 0x2514); // ┌ ┐ ┘ └
else if (STRCMP(token, "double") == 0)
pum_set_border_chars(0x2550, 0x2551, 0x2550, 0x2551, // ═ ║ ═ ║
0x2554, 0x2557, 0x255D, 0x255A); // ╔ ╗ ╝ ╚
else if (STRCMP(token, "round") == 0)
pum_set_border_chars(0x2500, 0x2502, 0x2500, 0x2502, // ─ │ ─ │
0x256d, 0x256e, 0x256f, 0x2570); // ╭ ╮ ╯ ╰
else if (STRCMP(token, "ascii") == 0)
pum_set_border_chars('-', '|', '-', '|', '+', '+', '+', '+');
else if (STRNCMP(token, "custom:", 7) == 0)
{
char_u *q = token + 7;
int out[8];
for (int i = 0; i < 8; i++)
{
if (*q == NUL || *q == ',')
goto error;
out[i] = mb_ptr2char(q);
mb_ptr2char_adv(&q);
if (i < 7)
{
if (*q != ';')
goto error; // must be semicolon
q++;
}
}
if (*q != NUL && *q != ',') // must end exactly after the 8th char
goto error;
pum_set_border_chars(out[0], out[1], out[2], out[3], out[4], out[5],
out[6], out[7]);
}
}
else if (STRCMP(token, "shadow") == 0)
if (STRNCMP(p, "shadow", len) == 0 && len == 6)
pum_set_shadow(TRUE);
else if (STRCMP(token, "margin") == 0)
else if (STRNCMP(p, "margin", len) == 0 && len == 6)
{
have_margin = TRUE;
pum_set_margin(TRUE);
}
else
{
vim_free(token);
goto error;
if (have_border)
goto error;
have_border = TRUE;
if (parse_pumopt_border(p, len) == FAIL)
goto error;
}
vim_free(token);
if (comma != NULL)
p = comma + 1; // move to next token (skip comma)
p = comma + 1;
else
break;
}
if (have_margin && !have_border)
goto error; // margin must be combined with border
goto error;
return NULL;
@@ -3817,12 +3952,12 @@ error:
int
expand_set_pumborder(optexpand_T *args, int *numMatches, char_u ***matches)
{
static char *(p_rlc_values[]) = {"single", "double", "round", "ascii",
static char *(p_pb_values[]) = {"single", "double", "round", "ascii",
"custom", "shadow", "margin", NULL};
return expand_set_opt_string(
args,
p_rlc_values,
ARRAY_LENGTH(p_rlc_values) - 1,
p_pb_values,
ARRAY_LENGTH(p_pb_values) - 1,
numMatches,
matches);
}
+4 -1
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim\n"
"Report-Msgid-Bugs-To: vim-dev@vim.org\n"
"POT-Creation-Date: 2026-04-07 18:24+0000\n"
"POT-Creation-Date: 2026-04-07 19:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -10015,6 +10015,9 @@ msgstr ""
msgid "popup border style"
msgstr ""
msgid "additional options for the popup menu"
msgstr ""
msgid "user defined function for Insert mode completion"
msgstr ""
+152 -13
View File
@@ -406,11 +406,17 @@ pum_call_update_screen(void)
int
pum_under_menu(int row, int col, int only_redrawing)
{
int extra_left = pum_border + (pum_margin && pum_border ? 1 : 0);
int extra_right = pum_border + (pum_margin && pum_border ? 1 : 0)
+ (pum_shadow ? 2 : 0);
int extra_above = pum_border;
int extra_below = pum_border + (pum_shadow ? 1 : 0);
return (!only_redrawing || pum_will_redraw)
&& row >= pum_row
&& row < pum_row + pum_height
&& col >= pum_col - 1
&& col < pum_col + pum_width + pum_scrollbar;
&& row >= pum_row - extra_above
&& row < pum_row + pum_height + extra_below
&& col >= pum_col - 1 - extra_left
&& col < pum_col + pum_width + pum_scrollbar + extra_right;
}
/*
@@ -878,6 +884,42 @@ pum_align_order(int *order)
order[2] = is_default ? CPT_MENU : cia_flags % 10;
}
static void pum_free_bg(void);
/*
* Called when the pum opacity value has changed.
* Invalidates cached background and triggers redraw if pum is visible.
*/
void
pum_opacity_changed(void)
{
// Invalidate cached background so it gets re-saved.
pum_free_bg();
if (pum_visible())
{
// Force full screen clear so ScreenAttrs doesn't retain
// stale blended values from the previous pumopacity.
redraw_all_later(UPD_CLEAR);
call_update_screen = TRUE;
pum_redraw();
}
}
static void
pum_free_bg(void)
{
int k;
VIM_CLEAR(pum_bg_attrs);
VIM_CLEAR(pum_bg_lines);
VIM_CLEAR(pum_bg_linesUC);
for (k = 0; k < MAX_MCO; ++k)
VIM_CLEAR(pum_bg_linesC[k]);
pum_bg_top = 0;
pum_bg_bot = 0;
pum_bg_cols = 0;
}
/*
* Redraw the popup menu, using "pum_first" and "pum_selected".
*/
@@ -906,6 +948,7 @@ pum_redraw(void)
int orig_attr = -1;
int scroll_range = pum_size - pum_height;
bool override_success;
int opacity_active = (p_po > 0 && p_po < 100);
// Use current window for highlight overrides when using 'winhighlight'
override_success = push_highlight_overrides(curwin->w_hl, curwin->w_hl_len);
@@ -925,11 +968,97 @@ pum_redraw(void)
if (call_update_screen)
{
call_update_screen = FALSE;
// Do not redraw in pum_may_redraw() and don't draw in the area where
// the popup menu will be.
pum_will_redraw = TRUE;
update_screen(0);
pum_will_redraw = FALSE;
// Invalidate cached background if screen size changed (e.g.
// after window resize).
if (opacity_active && pum_bg_lines != NULL
&& (pum_bg_cols != screen_Columns
|| pum_bg_bot > screen_Rows))
pum_free_bg();
if (opacity_active && pum_bg_lines != NULL)
{
// Already have saved background; skip update_screen to avoid
// flickering. Just do a normal pum_will_redraw update.
pum_will_redraw = TRUE;
update_screen(0);
pum_will_redraw = FALSE;
}
else if (opacity_active)
{
// For opacity: draw background including the area under the
// pum, then save it.
pum_pretend_not_visible = TRUE;
update_screen(0);
pum_pretend_not_visible = FALSE;
// Save background to static buffers.
if (ScreenLines != NULL && ScreenAttrs != NULL)
{
int save_top, save_bot, save_ncells, k;
pum_free_bg();
save_top = pum_row - pum_border;
save_bot = pum_row + pum_height + pum_border
+ (pum_shadow ? 1 : 0) + 1;
if (save_top < 0)
save_top = 0;
if (save_bot > screen_Rows)
save_bot = screen_Rows;
pum_bg_top = save_top;
pum_bg_bot = save_bot;
pum_bg_cols = screen_Columns;
if (save_top < save_bot)
{
save_ncells = (save_bot - save_top) * screen_Columns;
pum_bg_attrs = LALLOC_MULT(sattr_T, save_ncells);
pum_bg_lines = LALLOC_MULT(schar_T, save_ncells);
if (enc_utf8)
{
pum_bg_linesUC = LALLOC_MULT(u8char_T, save_ncells);
for (k = 0; k < MAX_MCO; ++k)
pum_bg_linesC[k] = LALLOC_MULT(u8char_T,
save_ncells);
}
if (pum_bg_attrs != NULL && pum_bg_lines != NULL)
{
for (int r = save_top; r < save_bot; ++r)
{
int soff = (r - save_top) * screen_Columns;
int loff = LineOffset[r];
mch_memmove(pum_bg_attrs + soff,
ScreenAttrs + loff,
screen_Columns * sizeof(sattr_T));
mch_memmove(pum_bg_lines + soff,
ScreenLines + loff,
screen_Columns * sizeof(schar_T));
if (enc_utf8 && pum_bg_linesUC != NULL
&& ScreenLinesUC != NULL)
{
mch_memmove(pum_bg_linesUC + soff,
ScreenLinesUC + loff,
screen_Columns * sizeof(u8char_T));
for (k = 0; k < MAX_MCO; ++k)
if (pum_bg_linesC[k] != NULL
&& ScreenLinesC[k] != NULL)
mch_memmove(pum_bg_linesC[k] + soff,
ScreenLinesC[k] + loff,
screen_Columns
* sizeof(u8char_T));
}
}
}
}
}
}
else
{
// Do not redraw in pum_may_redraw() and don't draw in the area
// where the popup menu will be.
pum_will_redraw = TRUE;
update_screen(0);
pum_will_redraw = FALSE;
}
}
// never display more than we have
@@ -950,12 +1079,18 @@ pum_redraw(void)
screen_zindex = POPUPMENU_ZINDEX;
#endif
// Draw border and shadow first if enabled
// Draw border and shadow first if enabled, before setting blend
// so that border/shadow characters are drawn without opacity.
if (pum_border)
pum_draw_border();
if (pum_shadow)
pum_draw_shadow();
// Set blend for screen_puts_len / screen_fill to use.
// Only the pum content area should be blended, not border/shadow.
if (opacity_active)
screen_pum_blend = 100 - (int)p_po;
for (i = 0; i < pum_height; ++i)
{
idx = i + pum_first;
@@ -967,7 +1102,8 @@ pum_redraw(void)
#ifdef FEAT_RIGHTLEFT
if (pum_rl)
{
if (pum_col < curwin->w_wincol + curwin->w_width - 1 - pum_border)
if (pum_col < curwin->w_wincol + curwin->w_width - 1
- pum_border)
screen_putchar(' ', row, pum_col + 1, attr);
}
else
@@ -1032,8 +1168,8 @@ pum_redraw(void)
#ifdef FEAT_RIGHTLEFT
if (pum_rl)
screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ',
' ', orig_attr);
screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1,
' ', ' ', orig_attr);
else
#endif
screen_fill(row, row + 1, col, pum_col + pum_width, ' ', ' ',
@@ -1043,6 +1179,8 @@ pum_redraw(void)
++row;
}
screen_pum_blend = 0;
#ifdef FEAT_PROP_POPUP
screen_zindex = 0;
#endif
@@ -1427,6 +1565,7 @@ pum_set_selected(int n, int repeat UNUSED)
void
pum_undisplay(void)
{
pum_free_bg();
pum_array = NULL;
redraw_all_later(UPD_NOT_VALID);
redraw_tabline = TRUE;
+1
View File
@@ -21,6 +21,7 @@ int get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg);
void clear_hl_tables(void);
int hl_combine_attr(int char_attr, int prim_attr);
int hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg);
int hl_pum_blend_attr(int char_attr, int popup_attr, int blend);
attrentry_T *syn_gui_attr2entry(int attr);
int syn_attr2attr(int attr);
attrentry_T *syn_term_attr2entry(int attr);
+2
View File
@@ -136,6 +136,8 @@ int expand_set_printoptions(optexpand_T *args, int *numMatches, char_u ***matche
char *did_set_renderoptions(optset_T *args);
char *did_set_rightleftcmd(optset_T *args);
int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_pumopt(optset_T *args);
int expand_set_pumopt(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_pumborder(optset_T *args);
int expand_set_pumborder(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_rulerformat(optset_T *args);
+1
View File
@@ -5,6 +5,7 @@ void pum_set_margin(int enable);
void pum_display(pumitem_T *array, int size, int selected);
void pum_call_update_screen(void);
int pum_under_menu(int row, int col, int only_redrawing);
void pum_opacity_changed(void);
void pum_redraw(void);
void pum_position_info_popup(win_T *wp);
void pum_undisplay(void);
+28 -2
View File
@@ -2653,6 +2653,34 @@ screen_fill(
}
skip_opacity_fill:
#endif
// For pum opacity: blend pum background with underlying.
// Only for space cells; text cells are handled normally.
if (screen_pum_blend > 0 && c == ' '
&& pum_bg_attrs != NULL
&& row >= pum_bg_top && row < pum_bg_bot
&& col < pum_bg_cols)
{
int soff = (row - pum_bg_top) * pum_bg_cols + col;
int underlying_attr = pum_bg_attrs[soff];
// Restore underlying character so text shows through.
ScreenLines[off] = pum_bg_lines[soff];
if (enc_utf8 && pum_bg_linesUC != NULL
&& ScreenLinesUC != NULL)
{
int k;
ScreenLinesUC[off] = pum_bg_linesUC[soff];
for (k = 0; k < MAX_MCO; ++k)
if (pum_bg_linesC[k] != NULL
&& ScreenLinesC[k] != NULL)
ScreenLinesC[k][off] = pum_bg_linesC[k][soff];
}
// Keep underlying fg, blend bg only.
ScreenAttrs[off] = hl_pum_blend_attr(underlying_attr,
attr, screen_pum_blend);
screen_char(off, row, col);
goto next_col;
}
#if defined(FEAT_GUI) || defined(UNIX)
// The bold trick may make a single row of pixels appear in
// the next character. When a bold character is removed, the
@@ -2699,9 +2727,7 @@ skip_opacity_fill:
if (!did_delete || c != ' ')
screen_char(off, row, col);
}
#ifdef FEAT_PROP_POPUP
next_col:
#endif
ScreenCols[off] = -1;
++off;
if (col == start_col)
+20
View File
@@ -0,0 +1,20 @@
|h+0&#ffffff0|e|l@1|o| |w|o|r|l|d| @63
|h|e|l@1|o| |v|i|m| @65
|h|e|l@1|o| |o|p|a|c|i|t|y| @61
|h|e|l|p| |m|e| @67
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|h|e|l@1|o> @69
|h+0#0000001#e0e0e08|e|l@1|o| @9| +0#4040ff13#ffffff0@59
|h+0#0000001#ffd7ff255|e|l|p| @10| +0#4040ff13#ffffff0@59
|~| @73
|~| @73
|-+2#0000000&@1| |K|e|y|w|o|r|d| |c|o|m|p|l|e|t|i|o|n| |(|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |2| +0#0000000&@33
+20
View File
@@ -0,0 +1,20 @@
|h+0&#ffffff0|e|l@1|o| |w|o|r|l|d| @63
|h|e|l@1|o| |v|i|m| @65
|h|e|l@1|o| |o|p|a|c|i|t|y| @61
|h|e|l|p| |m|e| @67
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G
|R|O|U|N|D| @69
|h|e|l@1|o> @69
|h+0#0000001#e0e0e08|e|l@1|o| @9| +0#4040ff13#ffffff0@59
|h+0#0000001#ffd7ff255|e|l|p| @10| +0#4040ff13#ffffff0@59
|~| @73
|~| @73
|-+2#0000000&@1| |K|e|y|w|o|r|d| |c|o|m|p|l|e|t|i|o|n| |(|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |2| +0#0000000&@33
+46
View File
@@ -2420,4 +2420,50 @@ func Test_popup_shadow_hiddenchar()
call StopVimInTerminal(buf)
endfunc
" Test pumopt opacity with screendump: background text should show through
func Test_pumopt_opacity_screendump()
CheckScreendump
let lines =<< trim END
set pumopt=opacity:50
set completeopt=menu
call setline(1, ['hello world', 'hello vim', 'hello opacity', 'help me'])
for i in range(5)
call append(line('$'), repeat('BACKGROUND', 8))
endfor
normal gg
END
call writefile(lines, 'Xpumoptopacity', 'D')
let buf = RunVimInTerminal('-S Xpumoptopacity', {})
call TermWait(buf)
call term_sendkeys(buf, "Gohel\<C-N>")
call TermWait(buf, 100)
call VerifyScreenDump(buf, 'Test_pumopt_opacity_50', {})
call term_sendkeys(buf, "\<C-E>\<Esc>u")
call TermWait(buf)
call StopVimInTerminal(buf)
endfunc
" Test pumopt opacity:100 (fully opaque, same as default)
func Test_pumopt_opacity_100()
CheckScreendump
let lines =<< trim END
set pumopt=opacity:100
set completeopt=menu
call setline(1, ['hello world', 'hello vim', 'hello opacity', 'help me'])
for i in range(5)
call append(line('$'), repeat('BACKGROUND', 8))
endfor
normal gg
END
call writefile(lines, 'Xpumoptopacity100', 'D')
let buf = RunVimInTerminal('-S Xpumoptopacity100', {})
call TermWait(buf)
call term_sendkeys(buf, "Gohel\<C-N>")
call TermWait(buf, 100)
call VerifyScreenDump(buf, 'Test_pumopt_opacity_100', {})
call term_sendkeys(buf, "\<C-E>\<Esc>u")
call TermWait(buf)
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+5
View File
@@ -291,6 +291,11 @@ let test_values = {
\ 'double,margin,shadow', 'custom:─;│;─;│;┌;┐;┘;└,shadow',
\ 'ascii,margin'],
\ ['xxx', 'margin', 'margin,shadow', 'custom:', 'custom:+;']],
\ 'pumopt': [['', 'border:single', 'border:double', 'border:ascii',
\ 'height:10', 'width:20', 'maxwidth:30', 'opacity:50',
\ 'border:double,margin,shadow',
\ 'height:10,width:20,maxwidth:30,opacity:80'],
\ ['xxx', 'opacity:200', 'opacity:-1', 'margin']],
\ 'renderoptions': [[''], ['xxx']],
\ 'rightleftcmd': [['search'], ['xxx']],
\ 'rulerformat': [['', 'xxx'], ['%-', '%(', '%15(%%']],
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
318,
/**/
317,
/**/