diff options
author | Famiu Haque <famiuhaque@protonmail.com> | 2022-05-13 20:47:11 +0600 |
---|---|---|
committer | Famiu Haque <famiuhaque@protonmail.com> | 2022-05-18 09:27:08 +0600 |
commit | e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb (patch) | |
tree | d892e10e8e922e71ce822e7c6a5c41fad1dd73d1 | |
parent | b9b5577d6d8e07d1e39020c8fc05f817f2e81c66 (diff) | |
download | rneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.tar.gz rneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.tar.bz2 rneovim-e1bdb2a258cbe6c5cb981acc6bac82cd9e7706fb.zip |
feat(ui): add `'winbar'`
Adds support for a bar at the top of each window, enabled through the
`'winbar'` option.
Co-authored-by: Björn Linse <bjorn.linse@gmail.com>
-rw-r--r-- | runtime/doc/api.txt | 3 | ||||
-rw-r--r-- | runtime/doc/index.txt | 5 | ||||
-rw-r--r-- | runtime/doc/options.txt | 15 | ||||
-rw-r--r-- | runtime/doc/syntax.txt | 4 | ||||
-rw-r--r-- | runtime/doc/various.txt | 8 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 1 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 8 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 35 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 13 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 5 | ||||
-rw-r--r-- | src/nvim/highlight_defs.h | 6 | ||||
-rw-r--r-- | src/nvim/highlight_group.c | 2 | ||||
-rw-r--r-- | src/nvim/mouse.c | 18 | ||||
-rw-r--r-- | src/nvim/move.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 8 | ||||
-rw-r--r-- | src/nvim/option.c | 36 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/options.lua | 10 | ||||
-rw-r--r-- | src/nvim/screen.c | 170 | ||||
-rw-r--r-- | src/nvim/testdir/test_cursor_func.vim | 5 | ||||
-rw-r--r-- | src/nvim/window.c | 146 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 13 | ||||
-rw-r--r-- | test/functional/ui/cursor_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/ui/winbar_spec.lua | 180 |
26 files changed, 538 insertions, 169 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 812208ca31..820eaad99a 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -844,8 +844,11 @@ nvim_eval_statusline({str}, {*opts}) *nvim_eval_statusline()* Treated as single-width even if it isn't. • highlights: (boolean) Return highlight information. + • use_winbar: (boolean) Evaluate winbar instead of + statusline. • use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid} is ignored. + Mutually exclusive with {use_winbar}. Return: ~ Dictionary containing statusline information, with these diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 5c36eaf8e5..58d49b5843 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1501,8 +1501,9 @@ tag command action ~ |:recover| :rec[over] recover a file from a swap file |:redo| :red[o] redo one undone change |:redir| :redi[r] redirect messages to a file or register -|:redraw| :redr[aw] force a redraw of the display -|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) +|:redraw| :redr[aw] force a redraw of the display +|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) and + window bar(s) |:redrawtabline| :redrawt[abline] force a redraw of the tabline |:registers| :reg[isters] display the contents of registers |:resize| :res[ize] change current window height diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4d6f589714..4022bc4334 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2452,6 +2452,7 @@ A jump table for the options with a short description can be found at |Q_op|. item default Used for ~ stl:c ' ' or '^' statusline of the current window stlnc:c ' ' or '=' statusline of the non-current windows + wbr:c ' ' window bar horiz:c '─' or '-' horizontal separators |:split| horizup:c '┴' or '-' upwards facing horizontal separator horizdown:c '┬' or '-' downwards facing horizontal separator @@ -2492,6 +2493,7 @@ A jump table for the options with a short description can be found at |Q_op|. item highlight group ~ stl:c StatusLine |hl-StatusLine| stlnc:c StatusLineNC |hl-StatusLineNC| + wbr:c WinBar |hl-WinBar| or |hl-WinBarNC| horiz:c WinSeparator |hl-WinSeparator| horizup:c WinSeparator |hl-WinSeparator| horizdown:c WinSeparator |hl-WinSeparator| @@ -7045,6 +7047,19 @@ A jump table for the options with a short description can be found at |Q_op|. key is never used for the menu. This option is not used for <F10>; on Win32. + *'winbar'* *'wbr'* +'winbar' 'wbr' string (default empty) + global or local to window |global-local| + When non-empty, this option enables the window bar and determines its + contents. The window bar is a bar that's shown at the top of every + window with it enabled. The value of 'winbar' is evaluated like with + 'statusline'. + + When changing something that is used in 'winbar' that does not trigger + it to be updated, use |:redrawstatus|. + + This option cannot be set in a modeline when 'modelineexpr' is off. + *'winblend'* *'winbl'* 'winblend' 'winbl' number (default 0) local to window diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 0aceb30ce0..c3ad704bb7 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5244,6 +5244,10 @@ Whitespace "nbsp", "space", "tab", "multispace", "lead" and "trail" in 'listchars'. *hl-WildMenu* WildMenu Current match in 'wildmenu' completion. + *hl-WinBar* +WinBar Window bar of current window. + *hl-WinBarNC* +WinBarNC Window bar of not-current windows. *hl-User1* *hl-User1..9* *hl-User9* The 'statusline' syntax allows the use of 9 different highlights in the diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 75ee0fdfdf..0a25904104 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -30,10 +30,10 @@ CTRL-L Clears and redraws the screen. The redraw may happen function (or a mapping if 'lazyredraw' set). *:redraws* *:redrawstatus* -:redraws[tatus][!] Redraws the status line of the current window, or all - status lines if "!" is included. - Useful if 'statusline' includes an item that doesn't - cause automatic updating. +:redraws[tatus][!] Redraws the status line and window bar of the current + window, or all status lines and window bars if "!" is + included. Useful if 'statusline' or 'winbar' includes + an item that doesn't cause automatic updating. *:redrawt* *:redrawtabline* :redrawt[abline] Redraw the tabline. Useful to update the tabline when diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 78f8ed3cca..27228204e0 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -80,6 +80,7 @@ return { "maxwidth"; "fillchar"; "highlights"; + "use_winbar"; "use_tabline"; }; option = { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index dcede27bcb..7bd68f277b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1418,14 +1418,14 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err) } /// Get default statusline highlight for window -const char *get_default_stl_hl(win_T *wp) +const char *get_default_stl_hl(win_T *wp, bool use_winbar) { if (wp == NULL) { return "TabLineFill"; - } else if (wp == curwin) { - return "StatusLine"; + } else if (use_winbar) { + return (wp == curwin) ? "WinBar" : "WinBarNC"; } else { - return "StatusLineNC"; + return (wp == curwin) ? "StatusLine" : "StatusLineNC"; } } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a257dd6478..3c378e73e1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2274,8 +2274,9 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) /// - fillchar: (string) Character to fill blank spaces in the statusline (see /// 'fillchars'). Treated as single-width even if it isn't. /// - highlights: (boolean) Return highlight information. +/// - use_winbar: (boolean) Evaluate winbar instead of statusline. /// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid} -/// is ignored. +/// is ignored. Mutually exclusive with {use_winbar}. /// /// @param[out] err Error details, if any. /// @return Dictionary containing statusline information, with these keys: @@ -2294,6 +2295,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * int maxwidth; int fillchar = 0; Window window = 0; + bool use_winbar = false; bool use_tabline = false; bool highlights = false; @@ -2313,7 +2315,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * window = (Window)opts->winid.data.integer; } - if (HAS_KEY(opts->fillchar)) { if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0 || ((size_t)utf_ptr2len(opts->fillchar.data.string.data) @@ -2323,7 +2324,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * } fillchar = utf_ptr2char(opts->fillchar.data.string.data); } - if (HAS_KEY(opts->highlights)) { highlights = api_object_to_bool(opts->highlights, "highlights", false, err); @@ -2331,7 +2331,13 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * return result; } } + if (HAS_KEY(opts->use_winbar)) { + use_winbar = api_object_to_bool(opts->use_winbar, "use_winbar", false, err); + if (ERROR_SET(err)) { + return result; + } + } if (HAS_KEY(opts->use_tabline)) { use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err); @@ -2339,6 +2345,10 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * return result; } } + if (use_winbar && use_tabline) { + api_set_error(err, kErrorTypeValidation, "use_winbar and use_tabline are mutually exclusive"); + return result; + } win_T *wp, *ewp; @@ -2348,7 +2358,6 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * fillchar = ' '; } else { wp = find_window_by_handle(window, err); - if (wp == NULL) { api_set_error(err, kErrorTypeException, "unknown winid %d", window); return result; @@ -2356,8 +2365,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * ewp = wp; if (fillchar == 0) { - int attr; - fillchar = fillchar_status(&attr, wp); + if (use_winbar) { + fillchar = wp->w_p_fcs_chars.wbr; + } else { + int attr; + fillchar = fillchar_status(&attr, wp); + } } } @@ -2369,7 +2382,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * maxwidth = (int)opts->maxwidth.data.integer; } else { - maxwidth = (use_tabline || global_stl_height() > 0) ? Columns : wp->w_width; + maxwidth = (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width; } char buf[MAXPATHL]; @@ -2404,7 +2417,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * // add the default highlight at the beginning of the highlight list if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) { Dictionary hl_info = ARRAY_DICT_INIT; - grpname = get_default_stl_hl(wp); + grpname = get_default_stl_hl(wp, use_winbar); PUT(hl_info, "start", INTEGER_OBJ(0)); PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); @@ -2418,22 +2431,18 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * PUT(hl_info, "start", INTEGER_OBJ((char *)sp->start - buf)); if (sp->userhl == 0) { - grpname = get_default_stl_hl(wp); + grpname = get_default_stl_hl(wp, use_winbar); } else if (sp->userhl < 0) { grpname = (char *)syn_id2name(-sp->userhl); } else { snprintf(user_group, sizeof(user_group), "User%d", sp->userhl); grpname = user_group; } - PUT(hl_info, "group", CSTR_TO_OBJ(grpname)); - ADD(hl_values, DICTIONARY_OBJ(hl_info)); } - PUT(result, "highlights", ARRAY_OBJ(hl_values)); } - PUT(result, "str", CSTR_TO_OBJ((char *)buf)); return result; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2adeeadbca..9efa540210 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -233,6 +233,8 @@ typedef struct { #define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak' char_u *wo_stl; #define w_p_stl w_onebuf_opt.wo_stl // 'statusline' + char *wo_wbr; +#define w_p_wbr w_onebuf_opt.wo_wbr // 'winbar' int wo_scb; #define w_p_scb w_onebuf_opt.wo_scb // 'scrollbind' int wo_diff_saved; // options were saved for starting diff mode @@ -1235,6 +1237,7 @@ struct window_S { struct { int stl; int stlnc; + int wbr; int horiz; int horizup; int horizdown; @@ -1282,14 +1285,20 @@ struct window_S { // int w_winrow; // first row of window in screen int w_height; // number of rows in window, excluding - // status/command/winbar line(s) + // status/command line(s) int w_status_height; // number of status lines (0 or 1) + int w_winbar_height; // number of window bars (0 or 1) int w_wincol; // Leftmost column of window in screen. int w_width; // Width of window, excluding separation. int w_hsep_height; // Number of horizontal separator rows (0 or 1) int w_vsep_width; // Number of vertical separator columns (0 or 1). pos_save_T w_save_cursor; // backup of cursor pos and topline + int w_winrow_off; ///< offset from winrow to the inner window area + int w_wincol_off; ///< offset from wincol to the inner window area + ///< this includes float border but excludes special columns + ///< implemented in win_line() (i.e. signs, folds, numbers) + // inner size of window, which can be overridden by external UI int w_height_inner; int w_width_inner; @@ -1379,6 +1388,7 @@ struct window_S { linenr_T w_redraw_top; // when != 0: first line needing redraw linenr_T w_redraw_bot; // when != 0: last line needing redraw bool w_redr_status; // if true status line must be redrawn + bool w_redr_winbar; // if true window bar must be redrawn bool w_redr_border; // if true border must be redrawn // remember what is shown in the ruler for this window (if 'ruler' set) @@ -1408,6 +1418,7 @@ struct window_S { // A few options have local flags for P_INSECURE. uint32_t w_p_stl_flags; // flags for 'statusline' + uint32_t w_p_wbr_flags; // flags for 'winbar' uint32_t w_p_fde_flags; // flags for 'foldexpr' uint32_t w_p_fdt_flags; // flags for 'foldtext' int *w_p_cc_cols; // array of columns to highlight or NULL diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9c2069663d..f9c6b6fbb2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6405,7 +6405,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); - tv_dict_add_nr(dict, S_LEN("winbar"), 0); + tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 68c2e37f22..ad133b5b5f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3611,8 +3611,8 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) // necessary for a top border since `row` starts at -1 in that case. if (row < height + wp->w_border_adj[2]) { winid = wp->handle; - winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border - wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border + winrow = row + 1 + wp->w_winrow_off; // Adjust by 1 for top border + wincol = col + 1 + wp->w_wincol_off; // Adjust by 1 for left border if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { (void)mouse_comp_pos(wp, &row, &col, &lnum); col = vcol2col(wp, lnum, col); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index e845073c12..0510faa57d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8831,7 +8831,7 @@ static void ex_redraw(exarg_T *eap) ui_flush(); } -/// ":redrawstatus": force redraw of status line(s) +/// ":redrawstatus": force redraw of status line(s) and window bar(s) static void ex_redrawstatus(exarg_T *eap) { if (State & MODE_CMDPREVIEW) { @@ -8847,8 +8847,7 @@ static void ex_redrawstatus(exarg_T *eap) } else { status_redraw_curbuf(); } - update_screen(VIsual_active ? INVERTED : - 0); + update_screen(VIsual_active ? INVERTED : 0); RedrawingDisabled = r; p_lz = p; ui_flush(); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 0515842b61..d293865efd 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -111,7 +111,9 @@ typedef enum { HLF_NFLOAT, // Floating window HLF_MSG, // Message area HLF_BORDER, // Floating window border - HLF_COUNT, // MUST be the last one + HLF_WBR, // Window bars + HLF_WBRNC, // Window bars of not-current windows + HLF_COUNT, // MUST be the last one } hlf_T; EXTERN const char *hlf_names[] INIT(= { @@ -172,6 +174,8 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_NFLOAT] = "NormalFloat", [HLF_MSG] = "MsgArea", [HLF_BORDER] = "FloatBorder", + [HLF_WBR] = "WinBar", + [HLF_WBRNC] = "WinBarNC", }); diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 240a96cb4b..0047cb02d6 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -103,9 +103,11 @@ static const char *highlight_init_both[] = { "TabLineFill cterm=reverse gui=reverse", "TabLineSel cterm=bold gui=bold", "TermCursor cterm=reverse gui=reverse", + "WinBar cterm=bold gui=bold", "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", "default link VertSplit Normal", "default link WinSeparator VertSplit", + "default link WinBarNC WinBar", "default link EndOfBuffer NonText", "default link LineNrAbove LineNr", "default link LineNrBelow LineNr", diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 8038ba2cad..3a1cc6b557 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -68,12 +68,12 @@ bool is_mouse_key(int c) /// mouse was previously on a status line, then the status line may be dragged. /// /// If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the -/// cursor is moved unless the cursor was on a status line. +/// cursor is moved unless the cursor was on a status line or window bar. /// This function returns one of IN_UNKNOWN, IN_BUFFER, IN_STATUS_LINE or /// IN_SEP_LINE depending on where the cursor was clicked. /// /// If flags has MOUSE_MAY_STOP_VIS, then Visual mode will be stopped, unless -/// the mouse is on the status line of the same window. +/// the mouse is on the status line or window bar of the same window. /// /// If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since /// the last call. @@ -87,6 +87,7 @@ int jump_to_mouse(int flags, bool *inclusive, int which_button) { static int on_status_line = 0; // #lines below bottom of window static int on_sep_line = 0; // on separator right of window + static bool on_winbar = false; static int prev_row = -1; static int prev_col = -1; static win_T *dragwin = NULL; // window being dragged @@ -126,6 +127,9 @@ retnomove: if (on_sep_line) { return IN_SEP_LINE; } + if (on_winbar) { + return IN_OTHER_WIN | MOUSE_WINBAR; + } if (flags & MOUSE_MAY_STOP_VIS) { end_visual_mode(); redraw_curbuf_later(INVERTED); // delete the inversion @@ -155,9 +159,11 @@ retnomove: fdc = win_fdccol_count(wp); dragwin = NULL; - if (row == -1) { - return IN_OTHER_WIN; + if (row == -1 + wp->w_winbar_height) { + on_winbar = !!wp->w_winbar_height; + return IN_OTHER_WIN | (on_winbar ? MOUSE_WINBAR : 0); } + on_winbar = false; // winpos and height may change in win_enter()! if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) { @@ -253,6 +259,9 @@ retnomove: did_drag |= count; } return IN_SEP_LINE; // Cursor didn't move + } else if (on_winbar) { + // After a click on the window bar don't start Visual mode. + return IN_OTHER_WIN | MOUSE_WINBAR; } else { // keep_window_focus must be true // before moving the cursor for a left click, stop Visual mode @@ -497,6 +506,7 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) // exist. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp == fp->fr_win) { + *rowp -= wp->w_winrow_off - wp->w_winbar_height; return wp; } } diff --git a/src/nvim/move.c b/src/nvim/move.c index 11feb497ea..97ce92f6a9 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1016,7 +1016,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, col -= wp->w_leftcol; if (col >= 0 && col < wp->w_width) { - coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_border_adj[3]) + 1; + coloff = col - scol + (local ? 0 : wp->w_wincol + wp->w_wincol_off) + 1; } else { scol = ccol = ecol = 0; // character is left or right of the window @@ -1027,7 +1027,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, } } } - *rowp = (local ? 0 : wp->w_winrow + wp->w_border_adj[0]) + row + rowoff; + *rowp = (local ? 0 : wp->w_winrow + wp->w_winrow_off) + row + rowoff; *scolp = scol + coloff; *ccolp = ccol + coloff; *ecolp = ecol + coloff; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2826b7dad1..1692970a97 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1280,7 +1280,8 @@ static void normal_redraw(NormalState *s) validate_cursor(); if (VIsual_active) { - update_curbuf(INVERTED); // update inverted part + redraw_curbuf_later(INVERTED); // update inverted part + update_screen(INVERTED); } else if (must_redraw) { update_screen(0); } else if (redraw_cmdline || clear_cmdline) { @@ -1850,6 +1851,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) oap == NULL ? NULL : &(oap->inclusive), which_button); + // A click in the window bar has no side effects. + if (jump_flags & MOUSE_WINBAR) { + return false; + } + moved = (jump_flags & CURSOR_MOVED); in_status_line = (jump_flags & IN_STATUS_LINE); in_sep_line = (jump_flags & IN_SEP_LINE); diff --git a/src/nvim/option.c b/src/nvim/option.c index 8267bcf9da..2ee6c175bb 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2149,6 +2149,8 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags) switch ((int)options[opt_idx].indir) { case PV_STL: return &wp->w_p_stl_flags; + case PV_WBR: + return &wp->w_p_wbr_flags; case PV_FDE: return &wp->w_p_fde_flags; case PV_FDT: @@ -2930,8 +2932,8 @@ ambw_end: curbuf->b_help = (curbuf->b_p_bt[0] == 'h'); redraw_titles(); } - } else if (gvarp == &p_stl || varp == &p_tal || varp == &p_ruf) { - // 'statusline', 'tabline' or 'rulerformat' + } else if (gvarp == &p_stl || gvarp == (char_u **)&p_wbr || varp == &p_tal || varp == &p_ruf) { + // 'statusline', 'winbar', 'tabline' or 'rulerformat' int wid; if (varp == &p_ruf) { // reset ru_wid first @@ -2950,12 +2952,16 @@ ambw_end: errmsg = check_stl_option(p_ruf); } } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') { - // check 'statusline' or 'tabline' only if it doesn't start with "%!" + // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!" errmsg = check_stl_option(s); } if (varp == &p_ruf && errmsg == NULL) { comp_col(); } + // add / remove window bars for 'winbar' + if (gvarp == (char_u **)&p_wbr) { + set_winbar(); + } } else if (gvarp == &p_cpt) { // check if it is a valid value for 'complete' -- Acevedo for (s = *varp; *s;) { @@ -3570,6 +3576,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab fcs_tab[] = { { &wp->w_p_fcs_chars.stl, "stl", ' ' }, { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, + { &wp->w_p_fcs_chars.wbr, "wbr", ' ' }, { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─ { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // ┴ { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬ @@ -3612,15 +3619,15 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) if (*p_ambw == 'd') { // XXX: If ambiwidth=double then some characters take 2 columns, // which is forbidden (TUI limitation?). Set old defaults. - fcs_tab[2].def = '-'; fcs_tab[3].def = '-'; fcs_tab[4].def = '-'; - fcs_tab[5].def = '|'; + fcs_tab[5].def = '-'; fcs_tab[6].def = '|'; fcs_tab[7].def = '|'; - fcs_tab[8].def = '+'; - fcs_tab[9].def = '-'; - fcs_tab[12].def = '|'; + fcs_tab[8].def = '|'; + fcs_tab[9].def = '+'; + fcs_tab[10].def = '-'; + fcs_tab[13].def = '|'; } } @@ -4766,7 +4773,7 @@ static void check_redraw(uint32_t flags) bool doclear = (flags & P_RCLR) == P_RCLR; bool all = ((flags & P_RALL) == P_RALL || doclear); - if ((flags & P_RSTAT) || all) { // mark all status lines dirty + if ((flags & P_RSTAT) || all) { // mark all status lines and window bars dirty status_redraw_all(); } @@ -5814,6 +5821,9 @@ void unset_global_local_option(char *name, void *from) case PV_STL: clear_string_option(&((win_T *)from)->w_p_stl); break; + case PV_WBR: + clear_string_option((char_u **)&((win_T *)from)->w_p_wbr); + break; case PV_UL: buf->b_p_ul = NO_LOCAL_UNDOLEVEL; break; @@ -5891,6 +5901,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) return (char_u *)&(curwin->w_p_sbr); case PV_STL: return (char_u *)&(curwin->w_p_stl); + case PV_WBR: + return (char_u *)&(curwin->w_p_wbr); case PV_UL: return (char_u *)&(curbuf->b_p_ul); case PV_LW: @@ -5984,6 +5996,9 @@ static char_u *get_varp(vimoption_T *p) case PV_STL: return *curwin->w_p_stl != NUL ? (char_u *)&(curwin->w_p_stl) : p->var; + case PV_WBR: + return *curwin->w_p_wbr != NUL + ? (char_u *)&(curwin->w_p_wbr) : p->var; case PV_UL: return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL ? (char_u *)&(curbuf->b_p_ul) : p->var; @@ -6252,6 +6267,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_rlc = vim_strsave(from->wo_rlc); to->wo_sbr = vim_strsave(from->wo_sbr); to->wo_stl = vim_strsave(from->wo_stl); + to->wo_wbr = xstrdup(from->wo_wbr); to->wo_wrap = from->wo_wrap; to->wo_wrap_save = from->wo_wrap_save; to->wo_lbr = from->wo_lbr; @@ -6327,6 +6343,7 @@ static void check_winopt(winopt_T *wop) check_string_option(&wop->wo_fcs); check_string_option(&wop->wo_lcs); check_string_option(&wop->wo_ve); + check_string_option((char_u **)&wop->wo_wbr); } /// Free the allocated memory inside a winopt_T. @@ -6352,6 +6369,7 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_fcs); clear_string_option(&wop->wo_lcs); clear_string_option(&wop->wo_ve); + clear_string_option((char_u **)&wop->wo_wbr); } void didset_window_options(win_T *wp) diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e358a29622..f5c7ae07bb 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -618,6 +618,7 @@ EXTERN int p_stmp; // 'shelltemp' EXTERN int p_ssl; // 'shellslash' #endif EXTERN char_u *p_stl; // 'statusline' +EXTERN char *p_wbr; // 'winbar' EXTERN int p_sr; // 'shiftround' EXTERN char_u *p_shm; // 'shortmess' EXTERN char_u *p_sbr; // 'showbreak' @@ -897,6 +898,7 @@ enum { WV_FCS, WV_LCS, WV_WINBL, + WV_WBR, WV_COUNT, // must be the last one }; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 313cace4b2..eb92d47fc8 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2832,6 +2832,16 @@ return { defaults={if_true="menu"} }, { + full_name='winbar', abbreviation='wbr', + short_desc=N_("custom format for the window bar"), + type='string', scope={'global', 'window'}, + alloced=true, + modelineexpr=true, + redraw={'statuslines'}, + varname='p_wbr', + defaults={if_true=""} + }, + { full_name='winblend', abbreviation='winbl', short_desc=N_("Controls transparency level for floating windows"), type='number', scope={'window'}, diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 686516f17b..b2490fb9c5 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -276,25 +276,25 @@ void redrawWinline(win_T *wp, linenr_T lnum) } } -/* - * update all windows that are editing the current buffer - */ -void update_curbuf(int type) -{ - redraw_curbuf_later(type); - update_screen(type); -} - /// called when the status bars for the buffer 'buf' need to be updated void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf - && (wp->w_status_height || (wp == curwin && global_stl_height()))) { + if (wp->w_buffer != buf) { + continue; + } + bool redraw = false; + + if (wp->w_status_height || (wp == curwin && global_stl_height())) { wp->w_redr_status = true; - if (must_redraw < VALID) { - must_redraw = VALID; - } + redraw = true; + } + if (wp->w_winbar_height) { + wp->w_redr_winbar = true; + redraw = true; + } + if (redraw && must_redraw < VALID) { + must_redraw = VALID; } } } @@ -400,6 +400,9 @@ int update_screen(int type) if (wp->w_floating) { continue; } + if (wp->w_winrow + wp->w_winbar_height > valid) { + wp->w_redr_winbar = true; + } if (W_ENDROW(wp) > valid) { wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID); } @@ -431,6 +434,9 @@ int update_screen(int type) wp->w_redr_type = REDRAW_TOP; } else { wp->w_redr_type = NOT_VALID; + if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) { + wp->w_redr_winbar = true; + } if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) { wp->w_redr_status = true; } @@ -585,10 +591,13 @@ int update_screen(int type) win_update(wp, &providers); } - // redraw status line after the window to minimize cursor movement + // redraw status line and window bar after the window to minimize cursor movement if (wp->w_redr_status) { win_redr_status(wp); } + if (wp->w_redr_winbar) { + win_redr_winbar(wp); + } } end_search_hl(); @@ -743,6 +752,7 @@ static void win_update(win_T *wp, DecorProviders *providers) if (type >= NOT_VALID) { wp->w_redr_status = true; + wp->w_redr_winbar = true; wp->w_lines_valid = 0; } @@ -4528,42 +4538,55 @@ void rl_mirror(char_u *str) } } -/* - * mark all status lines for redraw; used after first :cd - */ +/// Mark all status lines and window bars for redraw; used after first :cd void status_redraw_all(void) { - if (global_stl_height()) { - curwin->w_redr_status = true; - redraw_later(curwin, VALID); - } else { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height) { - wp->w_redr_status = true; - redraw_later(wp, VALID); - } + bool is_stl_global = global_stl_height() != 0; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + bool redraw = false; + + if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)) { + wp->w_redr_status = true; + redraw = true; + } + if (wp->w_winbar_height) { + wp->w_redr_winbar = true; + redraw = true; + } + if (redraw) { + redraw_later(wp, VALID); } } } -/// Marks all status lines of the current buffer for redraw. +/// Marks all status lines and window bars of the current buffer for redraw. void status_redraw_curbuf(void) { status_redraw_buf(curbuf); } -/// Marks all status lines of the specified buffer for redraw. +/// Marks all status lines and window bars of the given buffer for redraw. void status_redraw_buf(buf_T *buf) { - if (global_stl_height() != 0 && curwin->w_buffer == buf) { - curwin->w_redr_status = true; - redraw_later(curwin, VALID); - } else { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == buf) { - wp->w_redr_status = true; - redraw_later(wp, VALID); - } + bool is_stl_global = global_stl_height() != 0; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_buffer != buf) { + continue; + } + bool redraw = false; + + if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)) { + wp->w_redr_status = true; + redraw = true; + } + if (wp->w_winbar_height) { + wp->w_redr_winbar = true; + redraw = true; + } + if (redraw) { + redraw_later(wp, VALID); } } } @@ -4577,6 +4600,9 @@ void redraw_statuslines(void) if (wp->w_redr_status) { win_redr_status(wp); } + if (wp->w_redr_winbar) { + win_redr_winbar(wp); + } } if (redraw_tabline) { draw_tabline(); @@ -5075,7 +5101,7 @@ static void redraw_custom_statusline(win_T *wp) entered = true; did_emsg = false; - win_redr_custom(wp, false); + win_redr_custom(wp, false, false); if (did_emsg) { // When there is an error disable the statusline, otherwise the // display is messed up with errors and a redraw triggers the problem @@ -5088,6 +5114,41 @@ static void redraw_custom_statusline(win_T *wp) entered = false; } +static void win_redr_winbar(win_T *wp) +{ + static bool entered = false; + + // Return when called recursively. This can happen when the winbar contains an expression + // that triggers a redraw. + if (entered) { + return; + } + entered = true; + + wp->w_redr_winbar = false; + if (wp->w_winbar_height == 0) { + // No window bar, do nothing. + } else if (!redrawing()) { + // Don't redraw right now, do it later. + wp->w_redr_winbar = true; + } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) { + int saved_did_emsg = did_emsg; + + did_emsg = false; + win_redr_custom(wp, true, false); + if (did_emsg) { + // When there is an error disable the winbar, otherwise the + // display is messed up with errors and a redraw triggers the problem + // again and again. + set_string_option_direct("winbar", -1, (char_u *)"", + OPT_FREE | (*wp->w_p_stl != NUL + ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); + } + did_emsg |= saved_did_emsg; + } + entered = false; +} + /// Only call if (wp->w_vsep_width != 0). /// /// @return true if the status line of window "wp" is connected to the status @@ -5224,11 +5285,9 @@ bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) return buf[0] != NUL; } -/* - * Redraw the status line or ruler of window "wp". - * When "wp" is NULL redraw the tab pages line from 'tabline'. - */ -static void win_redr_custom(win_T *wp, bool draw_ruler) +/// Redraw the status line, window bar or ruler of window "wp". +/// When "wp" is NULL redraw the tab pages line from 'tabline'. +static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler) { static bool entered = false; int attr; @@ -5269,6 +5328,14 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) attr = HL_ATTR(HLF_TPF); maxwidth = Columns; use_sandbox = was_set_insecurely(wp, "tabline", 0); + } else if (draw_winbar) { + stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr); + row = wp->w_winrow; + col = wp->w_wincol; + fillchar = wp->w_p_fcs_chars.wbr; + attr = (wp == curwin) ? HL_ATTR(HLF_WBR) : HL_ATTR(HLF_WBRNC); + maxwidth = wp->w_width_inner; + use_sandbox = was_set_insecurely(wp, "winbar", 0); } else { row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); @@ -5553,14 +5620,17 @@ void win_grid_alloc(win_T *wp) grid->Rows = rows; grid->Columns = cols; + wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height; + wp->w_wincol_off = wp->w_border_adj[3]; + if (want_allocation) { grid->target = grid_allocated; - grid->row_offset = wp->w_border_adj[0]; - grid->col_offset = wp->w_border_adj[3]; + grid->row_offset = wp->w_winrow_off; + grid->col_offset = wp->w_wincol_off; } else { grid->target = &default_grid; - grid->row_offset = wp->w_winrow; - grid->col_offset = wp->w_wincol; + grid->row_offset = wp->w_winrow + wp->w_winrow_off; + grid->col_offset = wp->w_wincol + wp->w_wincol_off; } // send grid resize event if: @@ -6220,7 +6290,7 @@ void draw_tabline(void) // Check for an error. If there is one we would loop in redrawing the // screen. Avoid that by making 'tabline' empty. did_emsg = false; - win_redr_custom(NULL, false); + win_redr_custom(NULL, false, false); if (did_emsg) { set_string_option_direct("tabline", -1, (char_u *)"", OPT_FREE, SID_ERROR); @@ -6516,7 +6586,7 @@ static void win_redr_ruler(win_T *wp, bool always) int save_called_emsg = called_emsg; called_emsg = false; - win_redr_custom(wp, true); + win_redr_custom(wp, false, true); if (called_emsg) { set_string_option_direct("rulerformat", -1, (char_u *)"", OPT_FREE, SID_ERROR); diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 9ba82e3b70..6c6a6290cb 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -102,10 +102,11 @@ func Test_screenpos() bwipe! call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) - " Needs WinBar " nmenu WinBar.TEST : - " call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) + setlocal winbar=TEST + call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) " nunmenu WinBar.TEST + setlocal winbar& endfunc func Test_screenpos_number() diff --git a/src/nvim/window.c b/src/nvim/window.c index 2c27a5d6a5..51208e7178 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -695,6 +695,7 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err) } wp->w_floating = 1; wp->w_status_height = 0; + wp->w_winbar_height = 0; wp->w_hsep_height = 0; wp->w_vsep_width = 0; @@ -780,10 +781,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } if (!ui_has(kUIMultigrid)) { - wp->w_height = MIN(wp->w_height, - Rows - 1 - (wp->w_border_adj[0] + wp->w_border_adj[2])); - wp->w_width = MIN(wp->w_width, - Columns - (wp->w_border_adj[1] + wp->w_border_adj[3])); + wp->w_height = MIN(wp->w_height, Rows - 1 - win_extra_height(wp)); + wp->w_width = MIN(wp->w_width, Columns - win_extra_width(wp)); } win_set_inner_size(wp); @@ -1138,15 +1137,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else { layout = FR_COL; - /* - * Check if we are able to split the current window and compute its - * height. - */ - // Current window requires at least 1 space. - wmh1 = p_wmh == 0 ? 1 : p_wmh; + // Check if we are able to split the current window and compute its height. + // Current window requires at least 1 space plus space for the window bar. + wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height; needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { - needed += p_wh - wmh1; + needed += p_wh - wmh1 + oldwin->w_winbar_height; } if (flags & (WSP_BOT | WSP_TOP)) { minheight = frame_minheight(topframe, NOWIN) + need_status; @@ -1155,8 +1151,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else if (p_ea) { minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status; prevfrp = oldwin->w_frame; - for (frp = oldwin->w_frame->fr_parent; frp != NULL; - frp = frp->fr_parent) { + for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_COL) { FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { @@ -1339,6 +1334,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) set_fraction(oldwin); } wp->w_fraction = oldwin->w_fraction; + wp->w_winbar_height = oldwin->w_winbar_height; if (flags & WSP_VERT) { wp->w_p_scr = curwin->w_p_scr; @@ -1416,9 +1412,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & (WSP_TOP | WSP_BOT)) { int new_fr_height = curfrp->fr_height - new_size; - if (!((flags & WSP_BOT) && p_ls == 0) && global_stl_height() == 0) { + if (!((flags & WSP_BOT) && p_ls == 0) && !is_stl_global) { new_fr_height -= STATUS_HEIGHT; - } else if (global_stl_height() > 0) { + } else if (is_stl_global) { if (flags & WSP_BOT) { frame_add_hsep(curfrp); } else { @@ -1452,7 +1448,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } - if ((flags & WSP_BOT) && global_stl_height() == 0) { + if ((flags & WSP_BOT) && !is_stl_global) { frame_add_statusline(curfrp); } frame_fix_height(wp); @@ -1482,10 +1478,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * equalize the window sizes. */ if (do_equal || dir != 0) { - win_equal(wp, true, - (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') - : dir == 'h' ? 'b' : - 'v'); + win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v')); } // Don't change the window height/width to 'winheight' / 'winwidth' if a @@ -1675,10 +1668,10 @@ int win_count(void) } /// Make "count" windows on the screen. -/// Must be called when there is just one window, filling the whole screen +/// Must be called when there is just one window, filling the whole screen. /// (excluding the command line). /// -/// @param vertical split windows vertically if true +/// @param vertical split windows vertically if true. /// /// @return actual number of windows on the screen. int make_windows(int count, bool vertical) @@ -1687,15 +1680,15 @@ int make_windows(int count, bool vertical) int todo; if (vertical) { - // Each window needs at least 'winminwidth' lines and a separator - // column. + // Each window needs at least 'winminwidth' lines and a separator column. maxcount = (curwin->w_width + curwin->w_vsep_width - (p_wiw - p_wmw)) / (p_wmw + 1); } else { - // Each window needs at least 'winminheight' lines - // If statusline isn't global, each window also needs a statusline + // Each window needs at least 'winminheight' lines. + // If statusline isn't global, each window also needs a statusline. + // If 'winbar' is set, each window also needs a winbar. maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); + - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); } if (maxcount < 2) { @@ -1705,17 +1698,13 @@ int make_windows(int count, bool vertical) count = maxcount; } - /* - * add status line now, otherwise first window will be too big - */ + // add status line now, otherwise first window will be too big if (count > 1) { last_status(true); } - /* - * Don't execute autocommands while creating the windows. Must do that - * when putting the buffers in the windows. - */ + // Don't execute autocommands while creating the windows. Must do that + // when putting the buffers in the windows. block_autocmds(); // todo is number of windows left to create @@ -1790,7 +1779,7 @@ static void win_exchange(long Prenum) * if wp != wp2 * 3. remove wp from the list * 4. insert wp after wp2 - * 5. exchange the status line height, hsep height and vsep width. + * 5. exchange the status line height, winbar height, hsep height and vsep width. */ wp2 = curwin->w_prev; frp2 = curwin->w_frame->fr_prev; @@ -1897,7 +1886,7 @@ static void win_rotate(bool upwards, int count) frame_insert(frp->fr_parent->fr_child, frp); } - // exchange status height, hsep height and vsep width of old and new last window + // exchange status height, winbar height, hsep height and vsep width of old and new last window n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; @@ -1988,7 +1977,8 @@ void win_move_after(win_T *win1, win_T *win2) return; } - // may need move the status line, horizontal or vertical separator of the last window + // may need move the status line, window bar, horizontal or vertical separator of the last + // window if (win1 == lastwin) { height = win1->w_prev->w_status_height; win1->w_prev->w_status_height = win1->w_status_height; @@ -2237,7 +2227,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmh + STATUS_HEIGHT); + totwincount = (n + extra_sep) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2272,7 +2262,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmh + STATUS_HEIGHT); + ? extra_sep : 0)) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); } room -= new_size - n; if (room < 0) { @@ -2318,7 +2308,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmh + STATUS_HEIGHT); + / (p_wmh + STATUS_HEIGHT + global_winbar_height()); m = frame_minheight(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -3732,13 +3722,9 @@ static void frame_fix_height(win_T *wp) wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height; } -/* - * Compute the minimal height for frame "topfrp". - * Uses the 'winminheight' option. - * When "next_curwin" isn't NULL, use p_wh for this window. - * When "next_curwin" is NOWIN, don't use at least one line for the current - * window. - */ +/// Compute the minimal height for frame "topfrp". Uses the 'winminheight' option. +/// When "next_curwin" isn't NULL, use p_wh for this window. +/// When "next_curwin" is NOWIN, don't use at least one line for the current window. static int frame_minheight(frame_T *topfrp, win_T *next_curwin) { frame_T *frp; @@ -3746,12 +3732,14 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) int n; if (topfrp->fr_win != NULL) { + // Combined height of window bar and separator column or status line. + int extra_height = topfrp->fr_win->w_winbar_height + topfrp->fr_win->w_hsep_height + + topfrp->fr_win->w_status_height; + if (topfrp->fr_win == next_curwin) { - m = p_wh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; + m = p_wh + extra_height; } else { - // window: minimal height of the window plus separator column or status line - // depending on whether global statusline is enabled - m = p_wmh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; + m = p_wmh + extra_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3976,7 +3964,7 @@ static void new_frame(win_T *wp) void win_init_size(void) { firstwin->w_height = ROWS_AVAIL; - firstwin->w_height_inner = firstwin->w_height; + firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height; firstwin->w_height_outer = firstwin->w_height; topframe->fr_height = ROWS_AVAIL; firstwin->w_width = Columns; @@ -4087,6 +4075,7 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_topframe = topframe; last_status(false); + set_winbar(); redraw_all_later(NOT_VALID); @@ -5460,16 +5449,9 @@ void win_setheight(int height) */ void win_setheight_win(int height, win_T *win) { - if (win == curwin) { - // Always keep current window at least one line high, even when - // 'winminheight' is zero. - if (height < p_wmh) { - height = p_wmh; - } - if (height == 0) { - height = 1; - } - } + // Always keep current window at least one line high, even when 'winminheight' is zero. + // Keep window at least two lines high if 'winbar' is enabled. + height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); if (win->w_floating) { win->w_float_config.height = height; @@ -6262,7 +6244,7 @@ void win_set_inner_size(win_T *wp) int prev_height = wp->w_height_inner; int height = wp->w_height_request; if (height == 0) { - height = wp->w_height; + height = wp->w_height - wp->w_winbar_height; } if (height != prev_height) { @@ -6279,8 +6261,9 @@ void win_set_inner_size(win_T *wp) set_fraction(wp); } } - wp->w_height_inner = height; wp->w_skipcol = 0; + wp->w_height_inner = height; + wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height; // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. @@ -6306,10 +6289,18 @@ void win_set_inner_size(win_T *wp) terminal_check_size(wp->w_buffer->terminal); } - wp->w_height_outer = (wp->w_height_inner - + wp->w_border_adj[0] + wp->w_border_adj[2]); - wp->w_width_outer = (wp->w_width_inner - + wp->w_border_adj[1] + wp->w_border_adj[3]); + wp->w_height_outer = (wp->w_height_inner + win_extra_height(wp)); + wp->w_width_outer = (wp->w_width_inner + win_extra_width(wp)); +} + +static int win_extra_height(win_T *wp) +{ + return wp->w_border_adj[0] + wp->w_border_adj[2]; +} + +static int win_extra_width(win_T *wp) +{ + return wp->w_border_adj[1] + wp->w_border_adj[3]; } /// Set the width of a window. @@ -6662,6 +6653,19 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) } } +// Add or remove window bars from windows depending on the value of 'winbar'. +void set_winbar(void) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + int winbar_height = (*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0; + if (wp->w_winbar_height != winbar_height) { + wp->w_winbar_height = winbar_height; + win_set_inner_size(wp); + wp->w_redr_winbar = winbar_height; + } + } +} + /// Return the number of lines used by the tab page line. int tabline_height(void) { @@ -6678,6 +6682,12 @@ int tabline_height(void) return 1; } +/// Return the number of lines used by default by the window bar. +int global_winbar_height(void) +{ + return *p_wbr != NUL ? 1 : 0; +} + /// Return the number of lines used by the global statusline int global_stl_height(void) { diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ba170ba8c5..c4748cc00d 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3102,6 +3102,19 @@ describe('API', function() 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight', { use_tabline = true, highlights = true })) end) + it('works with winbar', function() + eq({ + str = 'TextWithNoHighlightTextWithWarningHighlight', + width = 43, + highlights = { + { start = 0, group = 'WinBar' }, + { start = 19, group = 'WarningMsg' } + } + }, + meths.eval_statusline( + 'TextWithNoHighlight%#WarningMsg#TextWithWarningHighlight', + { use_winbar = true, highlights = true })) + end) end) end) describe('nvim_parse_cmd', function() diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 92300a8fa2..d61eebe3ea 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 62 + m.hl_id = 64 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 63 end + if m.id_lm then m.id_lm = 65 end end -- Assert the new expectation. diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua new file mode 100644 index 0000000000..6cf587d47b --- /dev/null +++ b/test/functional/ui/winbar_spec.lua @@ -0,0 +1,180 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local clear = helpers.clear +local command = helpers.command + +describe('winbar', function() + local screen + + before_each(function() + clear() + screen = Screen.new(60, 13) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true}, + [2] = {reverse = true}, + [3] = {bold = true, foreground = Screen.colors.Blue}, + [4] = {bold = true, reverse = true}, + [5] = {bold = true, foreground = Screen.colors.Red}, + [6] = {foreground = Screen.colors.Blue}, + }) + command('set winbar=Set\\ Up\\ The\\ Bars') + end) + it('works', function() + screen:expect([[ + {1:Set Up The Bars }| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]) + end) + it('works with custom \'fillchars\' value', function() + command('set fillchars=wbr:+') + screen:expect([[ + {1:Set Up The Bars+++++++++++++++++++++++++++++++++++++++++++++}| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]) + end) + it('works with custom highlight', function() + command('hi WinBar guifg=red') + screen:expect([[ + {5:Set Up The Bars }| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + | + ]]) + end) + it('works with splits', function() + command('hi WinBar guifg=red') + command('hi WinBarNC guifg=blue') + command('belowright vsplit | split | split') + screen:expect([[ + {6:Set Up The Bars }│{5:Set Up The Bars }| + │^ | + {3:~ }│{3:~ }| + {3:~ }│{4:[No Name] }| + {3:~ }│{6:Set Up The Bars }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }│{2:[No Name] }| + {3:~ }│{6:Set Up The Bars }| + {3:~ }│ | + {3:~ }│{3:~ }| + {2:[No Name] [No Name] }| + | + ]]) + end) + it('works when switching value of \'winbar\'', function() + command('belowright vsplit | split | split | set winbar=') + screen:expect([[ + │^ | + {3:~ }│{3:~ }| + {3:~ }│{3:~ }| + {3:~ }│{4:[No Name] }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }│{3:~ }| + {3:~ }│{2:[No Name] }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }│{3:~ }| + {2:[No Name] [No Name] }| + | + ]]) + command('set winbar=All\\ Your\\ Bar\\ Are\\ Belong\\ To\\ Us') + screen:expect([[ + {1:All Your Bar Are Belong To Us}│{1:All Your Bar Are Belong To Us }| + │^ | + {3:~ }│{3:~ }| + {3:~ }│{4:[No Name] }| + {3:~ }│{1:All Your Bar Are Belong To Us }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }│{2:[No Name] }| + {3:~ }│{1:All Your Bar Are Belong To Us }| + {3:~ }│ | + {3:~ }│{3:~ }| + {2:[No Name] [No Name] }| + | + ]]) + command('set winbar=Changed\\ winbar') + screen:expect([[ + {1:Changed winbar }│{1:Changed winbar }| + │^ | + {3:~ }│{3:~ }| + {3:~ }│{4:[No Name] }| + {3:~ }│{1:Changed winbar }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }│{2:[No Name] }| + {3:~ }│{1:Changed winbar }| + {3:~ }│ | + {3:~ }│{3:~ }| + {2:[No Name] [No Name] }| + | + ]]) + end) + it('works with laststatus=3', function() + command('set laststatus=3') + screen:expect([[ + {1:Set Up The Bars }| + ^ | + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {3:~ }| + {4:[No Name] }| + | + ]]) + command('belowright vsplit | split | split') + screen:expect([[ + {1:Set Up The Bars }│{1:Set Up The Bars }| + │^ | + {3:~ }│{3:~ }| + {3:~ }├──────────────────────────────| + {3:~ }│{1:Set Up The Bars }| + {3:~ }│ | + {3:~ }│{3:~ }| + {3:~ }├──────────────────────────────| + {3:~ }│{1:Set Up The Bars }| + {3:~ }│ | + {3:~ }│{3:~ }| + {4:[No Name] }| + | + ]]) + end) +end) |