diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 343c63f4b0..e68aafe051 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -479,6 +479,7 @@ to disable various visual features such as the 'number' column. Other highlight groups specific to floating windows: - |hl-FloatBorder| for window's border - |hl-FloatTitle| for window's title +- |hl-FloatFooter| for window's footer Currently, floating windows don't support some widgets like scrollbar. @@ -3138,11 +3139,18 @@ nvim_open_win({buffer}, {enter}, {*config}) *nvim_open_win()* specified by character: [ ["+", "MyCorner"], ["x", "MyBorder"] ]. - • title: Title (optional) in window border, String or list. - List is [text, highlight] tuples. if is string the default - highlight group is `FloatTitle`. - • title_pos: Title position must set with title option. - value can be of `left` `center` `right` default is left. + • title: Title (optional) in window border, string or list. + List should consist of `[text, highlight]` tuples. If + string, the default highlight group is `FloatTitle`. + • title_pos: Title position. Must be set with `title` + option. Value can be one of "left", "center", or "right". + Default is `"left"`. + • footer: Footer (optional) in window border, string or + list. List should consist of `[text, highlight]` tuples. + If string, the default highlight group is `FloatFooter`. + • footer_pos: Footer position. Must be set with `footer` + option. Value can be one of "left", "center", or "right". + Default is `"left"`. • noautocmd: If true then no buffer-related autocommand events such as |BufEnter|, |BufLeave| or |BufWinEnter| may fire from calling this function. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 56bdd07171..4f9f362b1e 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -155,6 +155,9 @@ The following new APIs and features were added. • New RPC client type `msgpack-rpc` is added for `nvim_set_client_info` to support fully MessagePack-RPC compliant clients. +• Floating windows can now show footer with new `footer` and `footer_pos` + config fields. Uses |hl-FloatFooter| by default. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 59f8235ad2..6c0c542737 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5273,6 +5273,8 @@ NormalFloat Normal text in floating windows. FloatBorder Border of floating windows. *hl-FloatTitle* FloatTitle Title of floating windows. + *hl-FloatFooter* +FloatFooter Footer of floating windows. *hl-NormalNC* NormalNC Normal text in non-current windows. *hl-Pmenu* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 29a7c50585..ab5e795c16 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -258,6 +258,7 @@ Highlight groups: |hl-NormalFloat| highlights floating window |hl-FloatBorder| highlights border of a floating window |hl-FloatTitle| highlights title of a floating window + |hl-FloatFooter| highlights footer of a floating window |hl-NormalNC| highlights non-current windows |hl-MsgArea| highlights messages/cmdline area |hl-MsgSeparator| highlights separator for scrolled messages diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index fdf5016b68..dd67fdb38b 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1551,11 +1551,18 @@ function vim.api.nvim_open_term(buffer, opts) end --- specified by character: [ ["+", "MyCorner"], ["x", --- "MyBorder"] ]. --- ---- • title: Title (optional) in window border, String or list. ---- List is [text, highlight] tuples. if is string the default ---- highlight group is `FloatTitle`. ---- • title_pos: Title position must set with title option. ---- value can be of `left` `center` `right` default is left. +--- • title: Title (optional) in window border, string or list. +--- List should consist of `[text, highlight]` tuples. If +--- string, the default highlight group is `FloatTitle`. +--- • title_pos: Title position. Must be set with `title` +--- option. Value can be one of "left", "center", or "right". +--- Default is `"left"`. +--- • footer: Footer (optional) in window border, string or +--- list. List should consist of `[text, highlight]` tuples. +--- If string, the default highlight group is `FloatFooter`. +--- • footer_pos: Footer position. Must be set with `footer` +--- option. Value can be one of "left", "center", or "right". +--- Default is `"left"`. --- • noautocmd: If true then no buffer-related autocommand --- events such as `BufEnter`, `BufLeave` or `BufWinEnter` may --- fire from calling this function. diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 941780202e..08c29ebe7a 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -108,6 +108,8 @@ error('Cannot require a meta file') --- @field border? any --- @field title? any --- @field title_pos? string +--- @field footer? any +--- @field footer_pos? string --- @field style? string --- @field noautocmd? boolean diff --git a/src/nvim/api/keysets.h b/src/nvim/api/keysets.h index a47e278cad..1f5c7069a9 100644 --- a/src/nvim/api/keysets.h +++ b/src/nvim/api/keysets.h @@ -99,6 +99,8 @@ typedef struct { Object border; Object title; String title_pos; + Object footer; + String footer_pos; String style; Boolean noautocmd; } Dict(float_config); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index b666d1efa2..ac27554172 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -145,11 +145,18 @@ /// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: /// [ ["+", "MyCorner"], ["x", "MyBorder"] ]. -/// - title: Title (optional) in window border, String or list. -/// List is [text, highlight] tuples. if is string the default -/// highlight group is `FloatTitle`. -/// - title_pos: Title position must set with title option. -/// value can be of `left` `center` `right` default is left. +/// - title: Title (optional) in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, the default highlight group is `FloatTitle`. +/// - title_pos: Title position. Must be set with `title` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. +/// - footer: Footer (optional) in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, the default highlight group is `FloatFooter`. +/// - footer_pos: Footer position. Must be set with `footer` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. /// - noautocmd: If true then no buffer-related autocommand events such as /// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from /// calling this function. @@ -240,6 +247,56 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) } } +Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig, + BorderTextType bordertext_type) +{ + VirtText chunks; + AlignTextPos align; + char *field_name; + char *field_pos_name; + switch (bordertext_type) { + case kBorderTextTitle: + chunks = fconfig->title_chunks; + align = fconfig->title_pos; + field_name = "title"; + field_pos_name = "title_pos"; + break; + case kBorderTextFooter: + chunks = fconfig->footer_chunks; + align = fconfig->footer_pos; + field_name = "footer"; + field_pos_name = "footer_pos"; + break; + } + + Array bordertext = ARRAY_DICT_INIT; + for (size_t i = 0; i < chunks.size; i++) { + Array tuple = ARRAY_DICT_INIT; + ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text)); + if (chunks.items[i].hl_id > 0) { + ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id))); + } + ADD(bordertext, ARRAY_OBJ(tuple)); + } + PUT(config, field_name, ARRAY_OBJ(bordertext)); + + char *pos; + switch (align) { + case kAlignLeft: + pos = "left"; + break; + case kAlignCenter: + pos = "center"; + break; + case kAlignRight: + pos = "right"; + break; + } + PUT(config, field_pos_name, CSTR_TO_OBJ(pos)); + + return config; +} + /// Gets window configuration. /// /// The returned value may be given to |nvim_open_win()|. @@ -301,26 +358,10 @@ Dictionary nvim_win_get_config(Window window, Error *err) } PUT(rv, "border", ARRAY_OBJ(border)); if (config->title) { - Array titles = ARRAY_DICT_INIT; - VirtText title_datas = config->title_chunks; - for (size_t i = 0; i < title_datas.size; i++) { - Array tuple = ARRAY_DICT_INIT; - ADD(tuple, CSTR_TO_OBJ(title_datas.items[i].text)); - if (title_datas.items[i].hl_id > 0) { - ADD(tuple, CSTR_TO_OBJ(syn_id2name(title_datas.items[i].hl_id))); - } - ADD(titles, ARRAY_OBJ(tuple)); - } - PUT(rv, "title", ARRAY_OBJ(titles)); - char *title_pos; - if (config->title_pos == kAlignLeft) { - title_pos = "left"; - } else if (config->title_pos == kAlignCenter) { - title_pos = "center"; - } else { - title_pos = "right"; - } - PUT(rv, "title_pos", CSTR_TO_OBJ(title_pos)); + rv = config_put_bordertext(rv, config, kBorderTextTitle); + } + if (config->footer) { + rv = config_put_bordertext(rv, config, kBorderTextFooter); } } } @@ -381,54 +422,91 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out) return true; } -static void parse_border_title(Object title, FloatConfig *fconfig, Error *err) +static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, + FloatConfig *fconfig, Error *err) { - if (title.type == kObjectTypeString) { - if (title.data.string.size == 0) { - fconfig->title = false; + bool *is_present; + VirtText *chunks; + int *width; + int default_hl_id; + switch (bordertext_type) { + case kBorderTextTitle: + is_present = &fconfig->title; + chunks = &fconfig->title_chunks; + width = &fconfig->title_width; + default_hl_id = syn_check_group(S_LEN("FloatTitle")); + break; + case kBorderTextFooter: + is_present = &fconfig->footer; + chunks = &fconfig->footer_chunks; + width = &fconfig->footer_width; + default_hl_id = syn_check_group(S_LEN("FloatFooter")); + break; + } + + if (bordertext.type == kObjectTypeString) { + if (bordertext.data.string.size == 0) { + *is_present = false; return; } - int hl_id = syn_check_group(S_LEN("FloatTitle")); - kv_push(fconfig->title_chunks, ((VirtTextChunk){ .text = xstrdup(title.data.string.data), - .hl_id = hl_id })); - fconfig->title_width = (int)mb_string2cells(title.data.string.data); - fconfig->title = true; + kv_push(*chunks, ((VirtTextChunk){ .text = xstrdup(bordertext.data.string.data), + .hl_id = default_hl_id })); + *width = (int)mb_string2cells(bordertext.data.string.data); + *is_present = true; return; } - if (title.type != kObjectTypeArray) { + if (bordertext.type != kObjectTypeArray) { api_set_error(err, kErrorTypeValidation, "title must be string or array"); return; } - if (title.data.array.size == 0) { + if (bordertext.data.array.size == 0) { api_set_error(err, kErrorTypeValidation, "title cannot be an empty array"); return; } - fconfig->title_width = 0; - fconfig->title_chunks = parse_virt_text(title.data.array, err, &fconfig->title_width); + *width = 0; + *chunks = parse_virt_text(bordertext.data.array, err, width); - fconfig->title = true; + *is_present = true; } -static bool parse_title_pos(String title_pos, FloatConfig *fconfig, Error *err) +static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type, + FloatConfig *fconfig, Error *err) { - if (title_pos.size == 0) { - fconfig->title_pos = kAlignLeft; + AlignTextPos *align; + switch (bordertext_type) { + case kBorderTextTitle: + align = &fconfig->title_pos; + break; + case kBorderTextFooter: + align = &fconfig->footer_pos; + break; + } + + if (bordertext_pos.size == 0) { + *align = kAlignLeft; return true; } - char *pos = title_pos.data; + char *pos = bordertext_pos.data; if (strequal(pos, "left")) { - fconfig->title_pos = kAlignLeft; + *align = kAlignLeft; } else if (strequal(pos, "center")) { - fconfig->title_pos = kAlignCenter; + *align = kAlignCenter; } else if (strequal(pos, "right")) { - fconfig->title_pos = kAlignRight; + *align = kAlignRight; } else { - api_set_error(err, kErrorTypeValidation, "invalid title_pos value"); + switch (bordertext_type) { + case kBorderTextTitle: + api_set_error(err, kErrorTypeValidation, "invalid title_pos value"); + break; + case kBorderTextFooter: + api_set_error(err, kErrorTypeValidation, "invalid footer_pos value"); + break; + } return false; } return true; @@ -518,8 +596,9 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) String str = style.data.string; if (str.size == 0 || strequal(str.data, "none")) { fconfig->border = false; - // title does not work with border equal none + // border text does not work with border equal none fconfig->title = false; + fconfig->footer = false; return; } for (size_t i = 0; defaults[i].name; i++) { @@ -693,13 +772,13 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, clear_virttext(&fconfig->title_chunks); } - parse_border_title(config->title, fconfig, err); + parse_bordertext(config->title, kBorderTextTitle, fconfig, err); if (ERROR_SET(err)) { return false; } // handles unset 'title_pos' same as empty string - if (!parse_title_pos(config->title_pos, fconfig, err)) { + if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) { return false; } } else { @@ -709,6 +788,33 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, } } + if (HAS_KEY_X(config, footer)) { + // footer only work with border + if (!HAS_KEY_X(config, border) && !fconfig->border) { + api_set_error(err, kErrorTypeException, "footer requires border to be set"); + return false; + } + + if (fconfig->footer) { + clear_virttext(&fconfig->footer_chunks); + } + + parse_bordertext(config->footer, kBorderTextFooter, fconfig, err); + if (ERROR_SET(err)) { + return false; + } + + // handles unset 'footer_pos' same as empty string + if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) { + return false; + } + } else { + if (HAS_KEY_X(config, footer_pos)) { + api_set_error(err, kErrorTypeException, "footer_pos requires footer to be set"); + return false; + } + } + if (HAS_KEY_X(config, border)) { parse_border_style(config->border, fconfig, err); if (ERROR_SET(err)) { diff --git a/src/nvim/api/win_config.h b/src/nvim/api/win_config.h index a4614f02ce..426a74fb3e 100644 --- a/src/nvim/api/win_config.h +++ b/src/nvim/api/win_config.h @@ -3,6 +3,7 @@ #include "nvim/api/keysets.h" #include "nvim/api/private/defs.h" +#include "nvim/buffer_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/win_config.h.generated.h" diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2d83242275..3f55dbbc00 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -936,6 +936,11 @@ typedef enum { kAlignRight = 2, } AlignTextPos; +typedef enum { + kBorderTextTitle = 0, + kBorderTextFooter = 1, +} BorderTextType; + typedef struct { Window window; lpos_T bufpos; @@ -948,14 +953,18 @@ typedef struct { int zindex; WinStyle style; bool border; - bool title; bool shadow; schar_T border_chars[8]; int border_hl_ids[8]; int border_attr[8]; + bool title; AlignTextPos title_pos; VirtText title_chunks; int title_width; + bool footer; + AlignTextPos footer_pos; + VirtText footer_chunks; + int footer_width; bool noautocmd; } FloatConfig; diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index bd177884fb..f71a47a596 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -701,20 +701,32 @@ void end_search_hl(void) screen_search_hl.rm.regprog = NULL; } -static void win_border_redr_title(win_T *wp, ScreenGrid *grid, int col) +static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText text_chunks, int row, int col) { - VirtText title_chunks = wp->w_float_config.title_chunks; - - for (size_t i = 0; i < title_chunks.size; i++) { - char *text = title_chunks.items[i].text; + for (size_t i = 0; i < text_chunks.size; i++) { + char *text = text_chunks.items[i].text; int cell = (int)mb_string2cells(text); - int hl_id = title_chunks.items[i].hl_id; + int hl_id = text_chunks.items[i].hl_id; int attr = hl_id ? syn_id2attr(hl_id) : 0; - grid_puts(grid, text, 0, col, attr); + grid_puts(grid, text, row, col, attr); col += cell; } } +int win_get_bordertext_col(int total_col, int text_width, AlignTextPos align) +{ + switch (align) { + case kAlignLeft: + return 1; + case kAlignCenter: + return (total_col - text_width) / 2 + 1; + case kAlignRight: + return total_col - text_width + 1; + default: + abort(); + } +} + static void win_redr_border(win_T *wp) { wp->w_redr_border = false; @@ -741,17 +753,9 @@ static void win_redr_border(win_T *wp) } if (wp->w_float_config.title) { - int title_col = 0; - int title_width = wp->w_float_config.title_width; - AlignTextPos title_pos = wp->w_float_config.title_pos; - - if (title_pos == kAlignCenter) { - title_col = (icol - title_width) / 2 + 1; - } else { - title_col = title_pos == kAlignLeft ? 1 : icol - title_width + 1; - } - - win_border_redr_title(wp, grid, title_col); + int title_col = win_get_bordertext_col(icol, wp->w_float_config.title_width, + wp->w_float_config.title_pos); + win_redr_bordertext(wp, grid, wp->w_float_config.title_chunks, 0, title_col); } if (adj[1]) { grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]); @@ -778,10 +782,17 @@ static void win_redr_border(win_T *wp) if (adj[3]) { grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]); } + for (int i = 0; i < icol; i++) { int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5; grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]); } + + if (wp->w_float_config.footer) { + int footer_col = win_get_bordertext_col(icol, wp->w_float_config.footer_width, + wp->w_float_config.footer_pos); + win_redr_bordertext(wp, grid, wp->w_float_config.footer_chunks, grid->rows - 1, footer_col); + } if (adj[1]) { grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]); } diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 2b268ff2e1..c4d140d1e1 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -173,6 +173,7 @@ static const char *highlight_init_both[] = { "default link NormalFloat Pmenu", "default link FloatBorder WinSeparator", "default link FloatTitle Title", + "default link FloatFooter Title", "default FloatShadow blend=80 guibg=Black", "default FloatShadowThrough blend=100 guibg=Black", "RedrawDebugNormal cterm=reverse gui=reverse", diff --git a/src/nvim/window.c b/src/nvim/window.c index 970027b2f8..e72c32700d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5239,8 +5239,9 @@ static void win_free(win_T *wp, tabpage_T *tp) } } - // free the border title text + // free the border text clear_virttext(&wp->w_float_config.title_chunks); + clear_virttext(&wp->w_float_config.footer_chunks); clear_matches(wp); diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 85795e7e17..6cdd6e51fa 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1851,6 +1851,39 @@ describe('float window', function() eq('center', title_pos) end) + it('validates footer footer_pos', function() + local buf = meths.create_buf(false,false) + eq("footer requires border to be set", + pcall_err(meths.open_win,buf, false, { + relative='editor', width=9, height=2, row=2, col=5, footer='Footer', + })) + eq("footer_pos requires footer to be set", + pcall_err(meths.open_win,buf, false, { + relative='editor', width=9, height=2, row=2, col=5, + border='single', footer_pos='left', + })) + end) + + it('validate footer_pos in nvim_win_get_config', function() + local footer_pos = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, false) + local opts = { + relative = 'editor', + col = 2, + row = 5, + height = 2, + width = 9, + border = 'double', + footer = 'Test', + footer_pos = 'center' + } + + local win_id = vim.api.nvim_open_win(bufnr, true, opts) + return vim.api.nvim_win_get_config(win_id).footer_pos + ]]) + + eq('center', footer_pos) + end) it('border with title', function() local buf = meths.create_buf(false, false) @@ -2033,6 +2066,368 @@ describe('float window', function() end end) + it('border with footer', function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, { + relative='editor', width=9, height=2, row=2, col=5, border="double", + footer = "Left",footer_pos = "left", + }) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚}{11:Left}{5:═════╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚}{11:Left}{5:═════╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= "Center",footer_pos="center"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═}{11:Center}{5:══╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═}{11:Center}{5:══╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= "Right",footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚════}{11:Right}{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚════}{11:Right}{5:╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {footer= { {"🦄"},{"BB"}},footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════}🦄BB{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════}🦄BB{5:╝}{0: }| + | + ]]} + end + end) + + it('border with title and footer', function() + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, { + relative='editor', width=9, height=2, row=2, col=5, border="double", + title = "Left", title_pos = "left", footer = "Right", footer_pos = "right", + }) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔}{11:Left}{5:═════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚════}{11:Right}{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔}{11:Left}{5:═════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚════}{11:Right}{5:╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= "Center",title_pos="center",footer= "Center",footer_pos="center"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═}{11:Center}{5:══╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═}{11:Center}{5:══╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═}{11:Center}{5:══╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═}{11:Center}{5:══╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= "Right",title_pos="right",footer= "Left",footer_pos="left"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔════}{11:Right}{5:╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚}{11:Left}{5:═════╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔════}{11:Right}{5:╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚}{11:Left}{5:═════╝}{0: }| + | + ]]} + end + + meths.win_set_config(win, {title= { {"🦄"},{"BB"}},title_pos="right",footer= { {"🦄"},{"BB"}},footer_pos="right"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {5:╔═════}🦄BB{5:╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════}🦄BB{5:╝}| + ]], float_pos={ + [4] = { { id = 1001 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔═════}🦄BB{5:╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════}🦄BB{5:╝}{0: }| + | + ]]} + end + end) + it('terminates border on edge of viewport when window extends past viewport', function() local buf = meths.create_buf(false, false) meths.open_win(buf, false, {relative='editor', width=40, height=7, row=0, col=0, border="single", zindex=201})