fix(highlight): preserve background transparency in 'winblend' #34302

Problem: When using 'winblend', transparent backgrounds (-1) are forced
to default colors (usually black) during attribute blending, breaking
the transparency effect.

Solution: Check original background colors before blending in
hl_blend_attrs(). If both background and foreground originally had
transparent backgrounds, preserve transparency instead of forcing
default colors.
This commit is contained in:
glepnir
2025-06-09 23:24:46 +08:00
committed by GitHub
parent 8fadb80b3a
commit 336b46a879
3 changed files with 100 additions and 82 deletions

View File

@@ -666,9 +666,8 @@ int hl_combine_attr(int char_attr, int prim_attr)
/// ///
/// If colors are unset, use builtin default colors. Never returns -1 /// If colors are unset, use builtin default colors. Never returns -1
/// Cterm colors are unchanged. /// Cterm colors are unchanged.
static HlAttrs get_colors_force(int attr) static HlAttrs get_colors_force(HlAttrs attrs)
{ {
HlAttrs attrs = syn_attr2entry(attr);
if (attrs.rgb_bg_color == -1) { if (attrs.rgb_bg_color == -1) {
attrs.rgb_bg_color = normal_bg; attrs.rgb_bg_color = normal_bg;
} }
@@ -704,7 +703,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
return front_attr; return front_attr;
} }
HlAttrs fattrs = get_colors_force(front_attr); HlAttrs fattrs_raw = syn_attr2entry(front_attr);
HlAttrs fattrs = get_colors_force(fattrs_raw);
int ratio = fattrs.hl_blend; int ratio = fattrs.hl_blend;
if (ratio <= 0) { if (ratio <= 0) {
*through = false; *through = false;
@@ -720,7 +720,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
return id; return id;
} }
HlAttrs battrs = get_colors_force(back_attr); HlAttrs battrs_raw = syn_attr2entry(back_attr);
HlAttrs battrs = get_colors_force(battrs_raw);
HlAttrs cattrs; HlAttrs cattrs;
if (*through) { if (*through) {
@@ -754,11 +755,13 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through)
cattrs.rgb_ae_attr &= ~HL_BG_INDEXED; cattrs.rgb_ae_attr &= ~HL_BG_INDEXED;
} }
cattrs.rgb_bg_color = rgb_blend(ratio, battrs.rgb_bg_color,
fattrs.rgb_bg_color);
// Check if we should preserve background transparency
// Use the raw attributes (before forcing colors) to check original transparency
cattrs.rgb_bg_color = (battrs_raw.rgb_bg_color == -1) && (fattrs_raw.rgb_bg_color == -1)
? -1
: rgb_blend(ratio, battrs.rgb_bg_color, fattrs.rgb_bg_color);
cattrs.hl_blend = -1; // blend property was consumed cattrs.hl_blend = -1; // blend property was consumed
HlKind kind = *through ? kHlBlendThrough : kHlBlend; HlKind kind = *through ? kHlBlendThrough : kHlBlend;
id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind, id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind,
.id1 = back_attr, .id2 = front_attr }); .id1 = back_attr, .id2 = front_attr });

View File

@@ -8439,6 +8439,13 @@ describe('float window', function()
[23] = { blend = 100, bold = true, foreground = Screen.colors.Magenta }, [23] = { blend = 100, bold = true, foreground = Screen.colors.Magenta },
[24] = { foreground = tonumber('0x7f007f'), bold = true, background = Screen.colors.White }, [24] = { foreground = tonumber('0x7f007f'), bold = true, background = Screen.colors.White },
[25] = { foreground = tonumber('0x7f007f'), bold = true, background = Screen.colors.Grey90 }, [25] = { foreground = tonumber('0x7f007f'), bold = true, background = Screen.colors.Grey90 },
[26] = { foreground = Screen.colors.Black },
[27] = { bold = true, foreground = tonumber('0x7f007f') },
[28] = { foreground = tonumber('0x990000') },
[29] = { foreground = Screen.colors.Gray20 },
[30] = { bold = true, foreground = tonumber('0x00007f') },
[31] = { foreground = Screen.colors.Red, blend = 80 },
[32] = { foreground = Screen.colors.Blue1, blend = 100, bold = true },
}) })
insert([[ insert([[
Lorem ipsum dolor sit amet, consectetur Lorem ipsum dolor sit amet, consectetur
@@ -8752,41 +8759,52 @@ describe('float window', function()
api.nvim_win_set_config(win, { border = 'single', title = 'Title', footer = 'Footer' }) api.nvim_win_set_config(win, { border = 'single', title = 'Title', footer = 'Footer' })
api.nvim_set_option_value('winblend', 100, { win = win }) api.nvim_set_option_value('winblend', 100, { win = win })
api.nvim_set_option_value('cursorline', true, { win = 0 }) api.nvim_set_option_value('cursorline', true, { win = 0 })
command('hi clear VertSplit') -- 'winblend' with transparent background. #18576
command('hi clear VertSplit | hi Normal guibg=NONE ctermbg=NONE')
api.nvim_win_set_option(win, 'winhighlight', 'Normal:Normal')
feed('k0') feed('k0')
if multigrid then if multigrid then
screen:expect { screen:expect({
grid = [[ grid = [[
## grid 1 ## grid 1
[2:--------------------------------------------------]|*8 [2:--------------------------------------------------]|*8
[3:--------------------------------------------------]| [3:--------------------------------------------------]|
## grid 2 ## grid 2
Ut enim ad minim veniam, quis nostrud | Ut enim ad minim veniam, quis nostrud |
exercitation ullamco laboris nisi ut aliquip ex | exercitation ullamco laboris nisi ut aliquip ex |
ea commodo consequat. Duis aute irure dolor in | ea commodo consequat. Duis aute irure dolor in |
reprehenderit in voluptate velit esse cillum | reprehenderit in voluptate velit esse cillum |
dolore eu fugiat nulla pariatur. Excepteur sint | dolore eu fugiat nulla pariatur. Excepteur sint |
occaecat cupidatat non proident, sunt in culpa | occaecat cupidatat non proident, sunt in culpa |
{16:^qui officia deserunt mollit anim id est }| {16:^qui officia deserunt mollit anim id est }|
laborum. | laborum. |
## grid 3 ## grid 3
| |
## grid 4 ## grid 4
{17:┌}{23:Title}{17:──────────┐}| {17:┌}{23:Title}{17:──────────┐}|
{17:│}{11:popup text}{18: }{17:│}| {17:│}{31:popup text}{17: │}|
{17:│}{19:~ }{17:│}|*2 {17:│}{32:~ }{17:│}|*2
{17:└}{23:Footer}{17:─────────┘}| {17:└}{23:Footer}{17:─────────┘}|
]], ]],
win_pos = { [2] = { height = 8, startcol = 0, startrow = 0, width = 50, win = 1000 } },
float_pos = { [4] = { 1001, 'NW', 1, 2, 5, true, 50, 1, 2, 5 } }, float_pos = { [4] = { 1001, 'NW', 1, 2, 5, true, 50, 1, 2, 5 } },
} win_viewport = {
[2] = { win = 1000, topline = 3, botline = 11, curline = 9, curcol = 0, linecount = 11, sum_scroll_delta = 3 },
[4] = { win = 1001, topline = 2, botline = 4, curline = 2, curcol = 7, linecount = 3, sum_scroll_delta = 2 },
},
win_viewport_margins = {
[2] = { bottom = 0, left = 0, right = 0, top = 0, win = 1000 },
[4] = { bottom = 1, left = 1, right = 1, top = 1, win = 1001 },
},
})
else else
screen:expect([[ screen:expect([[
Ut enim ad minim veniam, quis nostrud | Ut enim ad minim veniam, quis nostrud |
exercitation ullamco laboris nisi ut aliquip ex | exercitation ullamco laboris nisi ut aliquip ex |
ea co{20:┌}{24:Title}{20:──────────┐}Duis aute irure dolor in | ea co{26:┌}{27:Title}{26:──────────┐}Duis aute irure dolor in |
repre{20:│}{5:popup}{6:it i}{5:text}{20:lu│}tate velit esse cillum | repre{26:│}{28:popup}{29:it i}{28:text}{26:lu│}tate velit esse cillum |
dolor{20:│}{21:~}{20:eu fugiat null│} pariatur. Excepteur sint | dolor{26:│}{30:~}{26:eu fugiat null│} pariatur. Excepteur sint |
occae{20:│}{21:~}{20:t cupidatat no│} proident, sunt in culpa | occae{26:│}{30:~}{26:t cupidatat no│} proident, sunt in culpa |
{16:^qui o}{22:└}{25:Footer}{22:─────────┘}{16:ollit anim id est }| {16:^qui o}{22:└}{25:Footer}{22:─────────┘}{16:ollit anim id est }|
laborum. | laborum. |
| |

View File

@@ -2672,6 +2672,11 @@ describe('pager', function()
[10] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff') }, [10] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff') },
[11] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0x2b8452') }, [11] = { background = Screen.colors.Grey100, bold = true, foreground = tonumber('0x2b8452') },
[12] = { bold = true, reverse = true }, [12] = { bold = true, reverse = true },
[13] = { foreground = Screen.colors.Grey0 },
[14] = { foreground = Screen.colors.Grey90 },
[15] = { foreground = tonumber('0x00000c') },
[16] = { bold = true, foreground = tonumber('0xe5e5ff') },
[17] = { bold = true, foreground = tonumber('0x2b8452') },
}) })
command('set more') command('set more')
@@ -3005,60 +3010,52 @@ aliquip ex ea commodo consequat.]]
it('clears "-- more --" message', function() it('clears "-- more --" message', function()
command('hi MsgArea guisp=Yellow blend=10') command('hi MsgArea guisp=Yellow blend=10')
feed(':echon join(range(20), "\\n")<cr>') feed(':echon join(range(20), "\\n")<cr>')
screen:expect { screen:expect([[
grid = [[ {13:0}{14: }|
{7:0}{8: }| {15:1}{16: }|
{9:1}{10: }| {15:2}{16: }|
{9:2}{10: }| {15:3}{16: }|
{9:3}{10: }| {15:4}{16: }|
{9:4}{10: }| {15:5}{16: }|
{9:5}{10: }| {15:6}{16: }|
{9:6}{10: }| {17:--}{14: }{17:More}{14: }{17:--}{14:^ }|
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }| ]])
]],
}
feed('j') feed('j')
screen:expect { screen:expect([[
grid = [[ {13:1}{14: }|
{7:1}{8: }| {15:2}{16: }|
{9:2}{10: }| {15:3}{16: }|
{9:3}{10: }| {15:4}{16: }|
{9:4}{10: }| {15:5}{16: }|
{9:5}{10: }| {15:6}{16: }|
{9:6}{10: }| {15:7}{16: }|
{9:7}{10: }| {17:--}{14: }{17:More}{14: }{17:--}{14:^ }|
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }| ]])
]],
}
feed('k') feed('k')
screen:expect { screen:expect([[
grid = [[ {13:0}{14: }|
{7:0}{8: }| {15:1}{16: }|
{9:1}{10: }| {15:2}{16: }|
{9:2}{10: }| {15:3}{16: }|
{9:3}{10: }| {15:4}{16: }|
{9:4}{10: }| {15:5}{16: }|
{9:5}{10: }| {15:6}{16: }|
{9:6}{10: }| {17:--}{14: }{17:More}{14: }{17:--}{14:^ }|
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }| ]])
]],
}
feed('j') feed('j')
screen:expect { screen:expect([[
grid = [[ {13:1}{14: }|
{7:1}{8: }| {15:2}{16: }|
{9:2}{10: }| {15:3}{16: }|
{9:3}{10: }| {15:4}{16: }|
{9:4}{10: }| {15:5}{16: }|
{9:5}{10: }| {15:6}{16: }|
{9:6}{10: }| {15:7}{16: }|
{9:7}{10: }| {17:--}{14: }{17:More}{14: }{17:--}{14:^ }|
{11:--}{8: }{11:More}{8: }{11:--}{8:^ }| ]])
]],
}
end) end)
it('with :!cmd does not crash on resize', function() it('with :!cmd does not crash on resize', function()