From 746f5682a7d7c8e5cb122da5d68eacfd55d58887 Mon Sep 17 00:00:00 2001 From: Artem Krinitsyn Date: Sun, 19 Jul 2026 15:03:46 +0000 Subject: [PATCH] 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 --- src/nvim/drawscreen.c | 2 +- src/nvim/grid.c | 33 +- src/nvim/popupmenu.c | 2 +- test/functional/plugin/lsp/util_spec.lua | 54 +- test/functional/ui/float_spec.lua | 1269 +++++++++++----------- test/functional/ui/messages2_spec.lua | 8 +- test/functional/ui/screen.lua | 5 +- test/functional/ui/statusline_spec.lua | 114 +- 8 files changed, 752 insertions(+), 735 deletions(-) diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index e6fb58947a..f0a5bcaebf 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -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) { diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 19b0e7697f..97899181c1 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -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(); } diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 0631bdf1ce..6f1a147064 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -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++; diff --git a/test/functional/plugin/lsp/util_spec.lua b/test/functional/plugin/lsp/util_spec.lua index 4e6625af7b..9195f45d8c 100644 --- a/test/functional/plugin/lsp/util_spec.lua +++ b/test/functional/plugin/lsp/util_spec.lua @@ -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('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('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 | ]]) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index f42dd9b593..2b1fb1d733 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1117,37 +1117,37 @@ describe('float window', function() api.nvim_open_win(0, false, { relative = 'laststatus', border = 'single', anchor = 'SE', width = 5, height = 1, row = 0, col = 1000 }) local tabwin = api.nvim_open_win(0, false, { relative = 'tabline', border = 'single', width = 5, height = 1, row = 0, col = 1000 }) screen:expect([[ - ^ {2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + ^ {31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| {1:~ }|*3 - {1:~ }{2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + {1:~ }{31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| | ]]) command('tabnew | tabnext') screen:expect([[ {5: }{100:3}{5: Name] }{24: No Name]X}| - ^ {2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + ^ {31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| {1:~ }|*2 - {1:~ }{2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + {1:~ }{31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| | ]]) command('vsplit') screen:expect([[ {5: }{100:4}{5: Name] }{24: No Name]X}| - ^ {2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + ^ {31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| {1:~ }{2:│}{1:~}| - {1:~ }{2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + {1:~ }{31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| {3:[No Name] }{2:<}| | ]]) @@ -1156,25 +1156,25 @@ describe('float window', function() screen:expect([[ {5: }{100:3}{5: Name] }{24: No Name]X}| ^ | - {2:┌─────┐}{1: }| - {2:│}{4: }{2:│}{1: }| - {2:└─────┘}{1: }| + {31:┌─────┐}{1: }| + {31:│}{4: }{31:│}{1: }| + {31:└─────┘}{1: }| {1:~ }| - {1:~ }{2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + {1:~ }{31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| | ]]) command('tabonly') screen:expect([[ ^ | - {2:┌─────┐}{1: }| - {2:│}{4: }{2:│}{1: }| - {2:└─────┘}{1: }| + {31:┌─────┐}{1: }| + {31:│}{4: }{31:│}{1: }| + {31:└─────┘}{1: }| {1:~ }|*2 - {1:~ }{2:┌─────┐}| - {1:~ }{2:│}{4: }{2:│}| - {1:~ }{2:└─────┘}| + {1:~ }{31:┌─────┐}| + {1:~ }{31:│}{4: }{31:│}| + {1:~ }{31:└─────┘}| | ]]) end) @@ -1318,10 +1318,10 @@ describe('float window', function() local screen = Screen.new() local w1 = api.nvim_open_win(0, true, { relative = 'editor', border = 'single', row = 0, col = 0, width = 5, height = 5 }) screen:expect([[ - {2:┌─────┐} | - {2:│}{4:^ }{2:│}{1: }| - {2:│}{11:~ }{2:│}{1: }|*4 - {2:└─────┘}{1: }| + {31:┌─────┐} | + {31:│}{4:^ }{31:│}{1: }| + {31:│}{11:~ }{31:│}{1: }|*4 + {31:└─────┘}{1: }| {1:~ }|*6 | ]]) @@ -1329,10 +1329,10 @@ describe('float window', function() screen:expect([[ | {1:~ }| - {1:~ }{2:┌────┐}{1: }| - {1:~ }{2:│}{4:^ }{2:│}{1: }| - {1:~ }{2:│}{11:~ }{2:│}{1: }|*3 - {1:~ }{2:└────┘}{1: }| + {1:~ }{31:┌────┐}{1: }| + {1:~ }{31:│}{4:^ }{31:│}{1: }| + {1:~ }{31:│}{11:~ }{31:│}{1: }|*3 + {1:~ }{31:└────┘}{1: }| {1:~ }|*5 | ]]) @@ -1354,7 +1354,7 @@ describe('float window', function() [8] = { bold = true, foreground = Screen.colors.SeaGreen4 }, [9] = { background = Screen.colors.LightGrey, underline = true }, [10] = { background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta }, - [11] = { bold = true, foreground = Screen.colors.Magenta }, + [11] = { bold = true, foreground = Screen.colors.Fuchsia, background = Screen.colors.LightMagenta }, [12] = { background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1 }, [13] = { background = Screen.colors.WebGray }, [14] = { foreground = Screen.colors.Brown }, @@ -1376,6 +1376,11 @@ describe('float window', function() [30] = { background = Screen.colors.Grey, foreground = Screen.colors.Blue4, bold = true }, [31] = { foreground = Screen.colors.Grey0 }, [32] = { background = Screen.colors.LightMagenta, foreground = Screen.colors.Brown }, + [33] = { background = Screen.colors.LightMagenta, reverse = true }, + [34] = { foreground = Screen.colors.Fuchsia, bold = true }, + [35] = { foreground = Screen.colors.White, background = Screen.colors.Red1, reverse = true }, + [36] = { foreground = Screen.colors.Fuchsia, background = Screen.colors.Red1, bold = true }, + [37] = { foreground = Screen.colors.SeaGreen, reverse = true, bold = true }, } screen:set_default_attr_ids(attrs) end) @@ -2116,10 +2121,10 @@ describe('float window', function() ## grid 3 | ## grid 4 - {5:x}{7:ååååååååå}{5:\}| + {33:x}{7:ååååååååå}{33:\}| {17:n̈̊}{1: halloj! }{17:n̈̊}| {17:n̈̊}{1: BORDAA }{17:n̈̊}| - {5:\}{7:ååååååååå}{5:x}| + {33:\}{7:ååååååååå}{33:x}| ]], float_pos = { [4] = { 1001, 'NW', 1, 2, 5, true, 50, 1, 2, 5 } }, win_viewport = { @@ -2132,10 +2137,10 @@ describe('float window', function() grid = [[ ^ | {0:~ }| - {0:~ }{5:x}{7:ååååååååå}{5:\}{0: }| + {0:~ }{33:x}{7:ååååååååå}{33:\}{0: }| {0:~ }{17:n̈̊}{1: halloj! }{17:n̈̊}{0: }| {0:~ }{17:n̈̊}{1: BORDAA }{17:n̈̊}{0: }| - {0:~ }{5:\}{7:ååååååååå}{5:x}{0: }| + {0:~ }{33:\}{7:ååååååååå}{33:x}{0: }| | ]], } @@ -2158,8 +2163,8 @@ describe('float window', function() ## grid 3 | ## grid 4 - {5:<}{1: halloj! }{5:>}| - {5:<}{1: BORDAA }{5:>}| + {33:<}{1: halloj! }{33:>}| + {33:<}{1: BORDAA }{33:>}| ]], float_pos = { [4] = { 1001, 'NW', 1, 2, 5, true, 50, 1, 2, 5 } }, win_viewport = { @@ -2176,8 +2181,8 @@ describe('float window', function() grid = [[ ^ | {0:~ }| - {0:~ }{5:<}{1: halloj! }{5:>}{0: }| - {0:~ }{5:<}{1: BORDAA }{5:>}{0: }| + {0:~ }{33:<}{1: halloj! }{33:>}{0: }| + {0:~ }{33:<}{1: BORDAA }{33:>}{0: }| {0:~ }|*2 | ]], @@ -2198,10 +2203,10 @@ describe('float window', function() ## grid 3 | ## grid 4 - {5:_________}| + {33:_________}| {1: halloj! }| {1: BORDAA }| - {5:---------}| + {33:---------}| ]], float_pos = { [4] = { 1001, 'NW', 1, 2, 5, true, 50, 1, 2, 5 } }, win_viewport = { @@ -2218,10 +2223,10 @@ describe('float window', function() grid = [[ ^ | {0:~ }| - {0:~ }{5:_________}{0: }| + {0:~ }{33:_________}{0: }| {0:~ }{1: halloj! }{0: }| {0:~ }{1: BORDAA }{0: }| - {0:~ }{5:---------}{0: }| + {0:~ }{33:---------}{0: }| | ]], } @@ -2350,10 +2355,10 @@ describe('float window', function() ## grid 3 | ## grid 4 - {5:╔}{11: