fix(ui): combine float border with window background #40796

Problem:
Float border highlight groups (FloatBorder, FloatTitle and FloatFooter)
fall back to Normal background highlight if no background color set.

Solution:
Combine border colors with window-local Normal highlight.

Fix #38330
This commit is contained in:
Artem Krinitsyn
2026-07-19 15:03:46 +00:00
committed by GitHub
parent 851656c628
commit 746f5682a7
8 changed files with 752 additions and 735 deletions

View File

@@ -680,7 +680,7 @@ int update_screen(void)
if (wp->w_redr_border || wp->w_redr_type >= UPD_NOT_VALID) {
grid_draw_border(&wp->w_grid_alloc, &wp->w_config, wp->w_border_adj, (int)wp->w_p_winbl,
wp->w_ns_hl_attr);
wp->w_ns_hl_attr, wp->w_hl_attr_normal);
}
if (wp->w_redr_type != 0) {

View File

@@ -1085,9 +1085,11 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
/// @param overflow Number of cells to skip.
static void grid_draw_bordertext(VirtText vt, int col, int winbl, const int *hl_attr,
BorderTextType bt, int overflow)
int win_hl_attr_normal, BorderTextType bt, int overflow)
{
int default_attr = hl_attr[bt == kBorderTextTitle ? HLF_BTITLE : HLF_BFOOTER];
default_attr = hl_combine_attr(win_hl_attr_normal, default_attr);
if (overflow > 0) {
grid_line_puts(1, "<", -1, hl_apply_winblend(winbl, default_attr));
col += 1;
@@ -1138,20 +1140,22 @@ static int get_bordertext_col(int total_col, int text_width, AlignTextPos align)
}
/// draw border on floating window grid
void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl, int *hl_attr)
void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl, int *hl_attr,
int win_hl_attr_normal)
{
int *attrs = config->border_attr;
int default_adj[4] = { 1, 1, 1, 1 };
if (adj == NULL) {
adj = default_adj;
}
schar_T chars[8];
int border_attrs[8];
if (!hl_attr) {
hl_attr = hl_attr_active;
}
for (int i = 0; i < 8; i++) {
chars[i] = schar_from_str(config->border_chars[i]);
border_attrs[i] = hl_combine_attr(win_hl_attr_normal, config->border_attr[i]);
}
int irow = grid->rows - adj[0] - adj[2];
@@ -1160,20 +1164,21 @@ void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl,
if (adj[0]) {
screengrid_line_start(grid, 0, 0);
if (adj[3]) {
grid_line_put_schar(0, chars[0], attrs[0]);
grid_line_put_schar(0, chars[0], border_attrs[0]);
}
for (int i = 0; i < icol; i++) {
grid_line_put_schar(i + adj[3], chars[1], attrs[1]);
grid_line_put_schar(i + adj[3], chars[1], border_attrs[1]);
}
if (config->title) {
int title_col = get_bordertext_col(icol, config->title_width, config->title_pos);
grid_draw_bordertext(config->title_chunks, title_col, winbl, hl_attr, kBorderTextTitle,
grid_draw_bordertext(config->title_chunks, title_col, winbl, hl_attr,
win_hl_attr_normal, kBorderTextTitle,
config->title_width - icol);
}
if (adj[1]) {
grid_line_put_schar(icol + adj[3], chars[2], attrs[2]);
grid_line_put_schar(icol + adj[3], chars[2], border_attrs[2]);
}
grid_line_flush();
}
@@ -1181,13 +1186,13 @@ void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl,
for (int i = 0; i < irow; i++) {
if (adj[3]) {
screengrid_line_start(grid, i + adj[0], 0);
grid_line_put_schar(0, chars[7], attrs[7]);
grid_line_put_schar(0, chars[7], border_attrs[7]);
grid_line_flush();
}
if (adj[1]) {
int ic = (i == 0 && !adj[0] && chars[2]) ? 2 : 3;
screengrid_line_start(grid, i + adj[0], 0);
grid_line_put_schar(icol + adj[3], chars[ic], attrs[ic]);
grid_line_put_schar(icol + adj[3], chars[ic], border_attrs[ic]);
grid_line_flush();
}
}
@@ -1195,21 +1200,21 @@ void grid_draw_border(ScreenGrid *grid, WinConfig *config, int *adj, int winbl,
if (adj[2]) {
screengrid_line_start(grid, irow + adj[0], 0);
if (adj[3]) {
grid_line_put_schar(0, chars[6], attrs[6]);
grid_line_put_schar(0, chars[6], border_attrs[6]);
}
for (int i = 0; i < icol; i++) {
int ic = (i == 0 && !adj[3] && chars[6]) ? 6 : 5;
grid_line_put_schar(i + adj[3], chars[ic], attrs[ic]);
grid_line_put_schar(i + adj[3], chars[ic], border_attrs[ic]);
}
if (config->footer) {
int footer_col = get_bordertext_col(icol, config->footer_width, config->footer_pos);
grid_draw_bordertext(config->footer_chunks, footer_col, winbl, hl_attr, kBorderTextFooter,
config->footer_width - icol);
grid_draw_bordertext(config->footer_chunks, footer_col, winbl, hl_attr, win_hl_attr_normal,
kBorderTextFooter, config->footer_width - icol);
}
if (adj[1]) {
grid_line_put_schar(icol + adj[3], chars[4], attrs[4]);
grid_line_put_schar(icol + adj[3], chars[4], border_attrs[4]);
}
grid_line_flush();
}

View File

@@ -686,7 +686,7 @@ void pum_redraw(void)
int scroll_range = pum_size - pum_height;
if (fconfig.border) {
grid_draw_border(&pum_grid, &fconfig, NULL, 0, NULL);
grid_draw_border(&pum_grid, &fconfig, NULL, 0, NULL, 0);
if (!fconfig.shadow) {
row++;
col_off++;

View File

@@ -1404,13 +1404,13 @@ describe('vim.lsp.util', function()
end)
feed('K')
screen:expect([[
┌───────────────────────────────────────────────────┐|
{4:^ }│|
│┌───┐{11: }|
││{4:foo}│{11: }|
│└───┘{11: }|
{11:~ }|*7
└───────────────────────────────────────────────────┘|
{4:┌───────────────────────────────────────────────────┐}|
{4:^ │}|
{4:│┌───┐}{11: }{4:│}|
{4:││foo│}{11: }{4:│}|
{4:│└───┘}{11: }{4:│}|
{4:│}{11:~ }{4:│}|*7
{4:└───────────────────────────────────────────────────┘}|
|
]])
end)
@@ -1435,9 +1435,9 @@ describe('vim.lsp.util', function()
]])
screen:expect([[
^ |
┌─────────┐{1: }|
{100:local}{101: }{102:foo}{1: }|
└─────────┘{1: }|
{4:┌─────────┐}{1: }|
{4:│}{100:local}{101: }{102:foo}{4:│}{1: }|
{4:└─────────┘}{1: }|
{1:~ }|*9
|
]])
@@ -1445,9 +1445,9 @@ describe('vim.lsp.util', function()
feed('<C-w>wG')
screen:expect([[
|
┌─────────┐{1: }|
{101:^```}{4: }│{1: }|
└─────────┘{1: }|
{4:┌─────────┐}{1: }|
{4:│}{101:^```}{4: │}{1: }|
{4:└─────────┘}{1: }|
{1:~ }|*9
|
]])
@@ -1462,9 +1462,9 @@ describe('vim.lsp.util', function()
]])
screen:expect([[
^ |
┌─────────┐{1: }|
{100:local}{101: }{102:foo}{1: }|
└─────────┘{1: }|
{4:┌─────────┐}{1: }|
{4:│}{100:local}{101: }{102:foo}{4:│}{1: }|
{4:└─────────┘}{1: }|
{1:~ }|*9
|
]])
@@ -1479,10 +1479,10 @@ describe('vim.lsp.util', function()
feed('<C-W>wG')
screen:expect([[
|
┌─────────┐{1: }|
{100:local}{101: }{102:bar}{1: }|
{101:^```}{4: }│{1: }|
└─────────┘{1: }|
{4:┌─────────┐}{1: }|
{4:│}{100:local}{101: }{102:bar}{4:│}{1: }|
{4:│}{101:^```}{4: │}{1: }|
{4:└─────────┘}{1: }|
{1:~ }|*8
|
]])
@@ -1500,13 +1500,13 @@ describe('vim.lsp.util', function()
]])
screen:expect([[
^ |
┌─────┐{1: }|
{4:1 }│{1: }|
{4:2 }│{1: }|
{4:3 }│{1: }|
{4:4 }│{1: }|
{4:5 }│{1: }|
└─────┘{1: }|
{4:┌─────┐}{1: }|
{4:1 │}{1: }|
{4:2 │}{1: }|
{4:3 │}{1: }|
{4:4 │}{1: }|
{4:5 │}{1: }|
{4:└─────┘}{1: }|
{1:~ }|*5
|
]])

File diff suppressed because it is too large Load Diff

View File

@@ -650,6 +650,12 @@ describe('messages2', function()
screen:add_extra_attr_ids({
[100] = { background = Screen.colors.Blue1, foreground = Screen.colors.Red },
[101] = { background = Screen.colors.Red1, foreground = Screen.colors.Blue1 },
[102] = {
background = Screen.colors.Blue,
foreground = Screen.colors.Red1,
reverse = true,
bold = true,
},
})
command('hi MsgArea guifg=Red guibg=Blue')
command('hi Search guifg=Blue guibg=Red')
@@ -671,7 +677,7 @@ describe('messages2', function()
screen:expect([[
|
{1:~ }|*11
{3: }|
{102: }|
{101:^foo}{100: }|
]])
end)

View File

@@ -28,7 +28,7 @@
-- * If the timeout expires, the last match error will be reported and the
-- test will fail.
--
-- The 30 most common highlight groups are predefined, see init_colors() below.
-- The 31 most common highlight groups are predefined, see init_colors() below.
-- In this case "5" is a predefined highlight associated with the set composed of one
-- attribute: bold. Note that since the {5:} markup is not a real part of the
-- screen, the delimiter "|" moved to the right. Also, the highlighting of the
@@ -155,6 +155,7 @@ local function _init_colors()
[28] = { foreground = Screen.colors.SlateBlue, underline = true },
[29] = { foreground = Screen.colors.SlateBlue, bold = true },
[30] = { background = Screen.colors.Red },
[31] = { background = Screen.colors.Plum1, reverse = true },
}
Screen._global_hl_names = {}
@@ -1845,7 +1846,7 @@ function Screen:_print_snapshot()
dict = '{ ' .. self:_pprint_attrs(a) .. ' }'
end
local keyval = (type(i) == 'number') and '[' .. tostring(i) .. ']' or i
if not (type(i) == 'number' and modify_attrs and i <= 30) then
if not (type(i) == 'number' and modify_attrs and i <= 31) then
table.insert(attrstrs, ' ' .. keyval .. ' = ' .. dict .. ',')
end
if modify_attrs then

View File

@@ -1321,13 +1321,13 @@ describe("'statusline' in floatwin", function()
command(set_stl)
local has_stl = [[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}{3:<Name] [+]}{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:│}{3:<Name] [+]}{4:│}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*10
{2:[No Name] }|
|
@@ -1338,12 +1338,12 @@ describe("'statusline' in floatwin", function()
api.nvim_win_set_config(win, { style = 'minimal' })
local without_stl = [[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*11
{2:[No Name] }|
|
@@ -1366,12 +1366,12 @@ describe("'statusline' in floatwin", function()
command(set_stl)
screen:expect([[
{24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}|
^ ┌──────────┐ |
{1:~}{4:1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
^ {4:┌──────────┐} |
{1:~}{4:1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*11
{3:[No Name] }|
|
@@ -1380,13 +1380,13 @@ describe("'statusline' in floatwin", function()
api.nvim_win_set_config(win2, { style = 'minimal' })
screen:expect([[
{5: }{101:2}{5:+ [No Name] }{24: }{102:2}{24:+ [No Name] }{2: }{24:X}|
┌──────────┐ |
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}{3:<Name] [+]}{1: }|
{1:~}└──────────┘{1: }|
{4:┌──────────┐} |
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:│}{3:<Name] [+]}{4:│}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*10
{2:[No Name] }|
|
@@ -1394,12 +1394,12 @@ describe("'statusline' in floatwin", function()
command('tabnext')
screen:expect([[
{24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}|
^ ┌──────────┐ |
{1:~}{4:1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
^ {4:┌──────────┐} |
{1:~}{4:1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*11
{3:[No Name] }|
|
@@ -1408,13 +1408,13 @@ describe("'statusline' in floatwin", function()
command('tabclose | set laststatus=2')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}{3:<Name] [+]}{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:│}{3:<Name] [+]}{4:│}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*10
{2:[No Name] }|
|
@@ -1422,12 +1422,12 @@ describe("'statusline' in floatwin", function()
command('set laststatus=0')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*12
|
]])
@@ -1435,12 +1435,12 @@ describe("'statusline' in floatwin", function()
command('set laststatus=3')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*11
{3:[No Name] [+] }|
|
@@ -1448,12 +1448,12 @@ describe("'statusline' in floatwin", function()
api.nvim_buf_set_name(buf, 'stl_test')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}{4:^1 }│{1: }|
{1:~}{4:2 }│{1: }|
{1:~}{4:3 }│{1: }|
{1:~}{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~}{4:┌──────────┐}{1: }|
{1:~}{4:^1 │}{1: }|
{1:~}{4:2 │}{1: }|
{1:~}{4:3 │}{1: }|
{1:~}{4:4 │}{1: }|
{1:~}{4:└──────────┘}{1: }|
{1:~ }|*11
{3:stl_test [+] }|
|