Merge pull request #24739 from echasnovski/win-footer

feature: add floating window footer text
This commit is contained in:
bfredl 2023-08-26 19:41:52 +02:00 committed by GitHub
commit a6d0535c15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 629 additions and 80 deletions

View File

@ -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.

View File

@ -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*

View File

@ -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*

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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)));
rv = config_put_bordertext(rv, config, kBorderTextTitle);
}
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));
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 {
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)) {

View File

@ -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"

View File

@ -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;

View File

@ -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]);
}

View File

@ -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",

View File

@ -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);

View File

@ -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})