Merge pull request #10090 from bfredl/floatpopup

api/window: add style="minimal" flag to disable unwanted UI features for simple floats
This commit is contained in:
Björn Linse 2019-07-07 21:35:55 +02:00 committed by GitHub
commit 524fe6205d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 237 additions and 30 deletions

View File

@ -284,14 +284,18 @@ highlighting, or |api-highlights|.
By default, floats will use |hl-NormalFloat| as normal highlight, which
links to |hl-Pmenu| in the builtin color scheme. The 'winhighlight' option can
be used to override it. Currently, floating windows don't support any visual
decorations like a border or additional widgets like scrollbar.
decorations like a border or additional widgets like scrollbar. By default,
floats will inherit options from the current window. This is not always
useful for some options, like 'number'. Use `style='minimal'` flag to
|nvim_open_win()| to disable many UI features that are unwanted for a simple
float, like end-of-buffer region or special columns.
Here is an example for creating a float with scratch buffer: >
let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, ["test", "text"])
let opts = {'relative': 'cursor', 'width': 10, 'height': 2, 'col': 0,
\ 'row': 1, 'anchor': 'NW'}
\ 'row': 1, 'anchor': 'NW', 'style': 'minimal'}
let win = nvim_open_win(buf, 0, opts)
" optional: change highlight, otherwise Pmenu is used
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')

View File

@ -1064,6 +1064,19 @@ fail:
/// - `external`: GUI should display the window as an external
/// top-level window. Currently accepts no other positioning
/// configuration together with this.
/// - `style`: Configure the apparance of the window. Currently only takes
/// one non-empty value:
/// - "minimal" Nvim will display the window with many UI options
/// disabled. This is useful when displaing a temporary
/// float where the text should not be edited. Disables
/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn',
/// 'spell' and 'list' options. 'signcolumn' is changed to
/// `auto`. The end-of-buffer region is hidden by setting
/// `eob` flag of 'fillchars' to a space char, and clearing
/// the |EndOfBuffer| region in 'winhighlight'.
///
/// top-level window. Currently accepts no other positioning
/// configuration together with this.
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
@ -1085,6 +1098,11 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
if (buffer > 0) {
nvim_win_set_buf(wp->handle, buffer, err);
}
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(wp);
didset_window_options(wp);
}
return wp->handle;
}

View File

@ -13,6 +13,7 @@
#include "nvim/vim.h"
#include "nvim/buffer.h"
#include "nvim/cursor.h"
#include "nvim/option.h"
#include "nvim/window.h"
#include "nvim/screen.h"
#include "nvim/move.h"
@ -475,6 +476,10 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err)
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(win);
didset_window_options(win);
}
}
/// Return window configuration.

View File

@ -2543,6 +2543,11 @@ void get_winopts(buf_T *buf)
} else
copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
if (curwin->w_float_config.style == kWinStyleMinimal) {
didset_window_options(curwin);
win_set_minimal_style(curwin);
}
// Set 'foldlevel' to 'foldlevelstart' if it's not negative.
if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;

View File

@ -972,7 +972,6 @@ struct matchitem {
};
typedef int FloatAnchor;
typedef int FloatRelative;
enum {
kFloatAnchorEast = 1,
@ -985,15 +984,20 @@ enum {
// SE -> kFloatAnchorSouth | kFloatAnchorEast
EXTERN const char *const float_anchor_str[] INIT(= { "NW", "NE", "SW", "SE" });
enum {
typedef enum {
kFloatRelativeEditor = 0,
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
};
} FloatRelative;
EXTERN const char *const float_relative_str[] INIT(= { "editor", "window",
"cursor" });
typedef enum {
kWinStyleUnused = 0,
kWinStyleMinimal, /// Minimal UI: no number column, eob markers, etc
} WinStyle;
typedef struct {
Window window;
int height, width;
@ -1002,12 +1006,14 @@ typedef struct {
FloatRelative relative;
bool external;
bool focusable;
WinStyle style;
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
.row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \
.focusable = true })
.focusable = true, \
.style = kWinStyleUnused })
// Structure to store last cursor position and topline. Used by check_lnums()
// and reset_lnums().

View File

@ -141,10 +141,12 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
HlAttrs attrs = HLATTRS_INIT;
bool available = false;
int syn_attr = syn_id2attr(final_id);
if (syn_attr != 0) {
attrs = syn_attr2entry(syn_attr);
available = true;
if (final_id > 0) {
int syn_attr = syn_id2attr(final_id);
if (syn_attr != 0) {
attrs = syn_attr2entry(syn_attr);
available = true;
}
}
if (HLF_PNI <= idx && idx <= HLF_PST) {
@ -176,15 +178,14 @@ void update_window_hl(win_T *wp, bool invalid)
// determine window specific background set in 'winhighlight'
bool float_win = wp->w_floating && !wp->w_float_config.external;
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] > 0) {
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
wp->w_hl_ids[HLF_INACTIVE],
!has_blend);
} else if (float_win && wp->w_hl_ids[HLF_NFLOAT] > 0) {
} else if (float_win && wp->w_hl_ids[HLF_NFLOAT] != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
// 'cursorline'
wp->w_hl_ids[HLF_NFLOAT], !has_blend);
} else if (wp->w_hl_id_normal > 0) {
} else if (wp->w_hl_id_normal != 0) {
wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, !has_blend);
} else {
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
@ -199,14 +200,14 @@ void update_window_hl(win_T *wp, bool invalid)
}
}
if (wp != curwin) {
if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] == 0) {
wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
wp->w_hl_attr_normal);
}
for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) {
int attr;
if (wp->w_hl_ids[hlf] > 0) {
if (wp->w_hl_ids[hlf] != 0) {
attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
} else {
attr = HL_ATTR(hlf);

View File

@ -3762,7 +3762,8 @@ static bool parse_winhl_opt(win_T *wp)
size_t nlen = (size_t)(colon-p);
char *hi = colon+1;
char *commap = xstrchrnul(hi, ',');
int hl_id = syn_check_group((char_u *)hi, (int)(commap-hi));
int len = (int)(commap-hi);
int hl_id = len ? syn_check_group((char_u *)hi, len) : -1;
if (strncmp("Normal", p, nlen) == 0) {
w_hl_id_normal = hl_id;

View File

@ -1612,7 +1612,8 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row,
}
}
int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, hl));
int attr = hl_combine_attr(wp->w_hl_attr_normal,
hl ? win_hl_attr(wp, hl) : 0);
if (wp->w_p_rl) {
grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n,

View File

@ -584,15 +584,43 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err)
wp->w_status_height = 0;
wp->w_vsep_width = 0;
// TODO(bfredl): use set_option_to() after merging #9110 ?
wp->w_p_nu = false;
wp->w_allbuf_opt.wo_nu = false;
win_config_float(wp, fconfig);
wp->w_pos_changed = true;
redraw_win_later(wp, VALID);
return wp;
}
void win_set_minimal_style(win_T *wp)
{
wp->w_p_nu = false;
wp->w_p_rnu = false;
wp->w_p_cul = false;
wp->w_p_cuc = false;
wp->w_p_spell = false;
wp->w_p_list = false;
// Hide EOB region: use " " fillchar and cleared highlighting
if (wp->w_p_fcs_chars.eob != ' ') {
char_u *old = wp->w_p_fcs;
wp->w_p_fcs = ((*old == NUL)
? (char_u *)xstrdup("eob: ")
: concat_str(old, (char_u *)",eob: "));
xfree(old);
}
if (wp->w_hl_ids[HLF_EOB] != -1) {
char_u *old = wp->w_p_winhl;
wp->w_p_winhl = ((*old == NUL)
? (char_u *)xstrdup("EndOfBuffer:")
: concat_str(old, (char_u *)",EndOfBuffer:"));
xfree(old);
}
if (wp->w_p_scl[0] != 'a') {
xfree(wp->w_p_scl);
wp->w_p_scl = (char_u *)xstrdup("auto");
}
}
void win_config_float(win_T *wp, FloatConfig fconfig)
{
wp->w_width = MAX(fconfig.width, 1);
@ -821,6 +849,20 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
"'focusable' key must be Boolean");
return false;
}
} else if (!strcmp(key, "style")) {
if (val.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"'style' key must be String");
return false;
}
if (val.data.string.data[0] == NUL) {
fconfig->style = kWinStyleUnused;
} else if (striequal(val.data.string.data, "minimal")) {
fconfig->style = kWinStyleMinimal;
} else {
api_set_error(err, kErrorTypeValidation,
"Invalid value of 'style' key");
}
} else {
api_set_error(err, kErrorTypeValidation,
"Invalid key '%s'", key);

View File

@ -35,6 +35,10 @@ describe('floating windows', function()
[15] = {background = Screen.colors.Grey20},
[16] = {background = Screen.colors.Grey20, bold = true, foreground = Screen.colors.Blue1},
[17] = {background = Screen.colors.Yellow},
[18] = {foreground = Screen.colors.Brown, background = Screen.colors.Grey20},
[19] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
[20] = {bold = true, foreground = Screen.colors.Brown},
[21] = {background = Screen.colors.Gray90},
}
it('behavior', function()
@ -182,7 +186,7 @@ describe('floating windows', function()
end
end)
it('defaults to nonumber and NormalFloat highlight', function()
it('defaults to NormalFloat highlight and inherited options', function()
command('set number')
command('hi NormalFloat guibg=#333333')
feed('ix<cr>y<cr><esc>gg')
@ -205,18 +209,18 @@ describe('floating windows', function()
{0:~ }|
{0:~ }|
## grid 3
{15:x }|
{15:y }|
{15: }|
{18: 1 }{15:x }|
{18: 2 }{15:y }|
{18: 3 }{15: }|
{16:~ }|
]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
else
screen:expect([[
{14: 1 }^x |
{14: 2 }y |
{14: 3 } {15:x } |
{0:~ }{15:y }{0: }|
{0:~ }{15: }{0: }|
{14: 3 } {18: 1 }{15:x } |
{0:~ }{18: 2 }{15:y }{0: }|
{0:~ }{18: 3 }{15: }{0: }|
{0:~ }{16:~ }{0: }|
|
]])
@ -242,7 +246,7 @@ describe('floating windows', function()
{0:~ }|
{0:~ }|
## grid 3
{15: }|
{18: 1 }{15: }|
{16:~ }|
{16:~ }|
{16:~ }|
@ -251,7 +255,7 @@ describe('floating windows', function()
screen:expect([[
{14: 1 }^x |
{14: 2 }y |
{14: 3 } {15: } |
{14: 3 } {18: 1 }{15: } |
{0:~ }{16:~ }{0: }|
{0:~ }{16:~ }{0: }|
{0:~ }{16:~ }{0: }|
@ -260,6 +264,126 @@ describe('floating windows', function()
end
end)
it("can use 'minimal' style", function()
command('set number')
command('set signcolumn=yes')
command('set cursorline')
command('hi NormalFloat guibg=#333333')
feed('ix<cr>y<cr><esc>gg')
local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'})
if multigrid then
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
|
## grid 2
{19: }{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } |
{0:~ }|
{0:~ }|
{0:~ }|
## grid 3
{15:x }|
{15:y }|
{15: }|
{15: }|
]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
else
screen:expect([[
{19: }{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } {15:x } |
{0:~ }{15:y }{0: }|
{0:~ }{15: }{0: }|
{0:~ }{15: }{0: }|
|
]])
end
-- signcolumn=yes still works if there actually are signs
command('sign define piet1 text=𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄ texthl=Search')
command('sign place 1 line=1 name=piet1 buffer=1')
if multigrid then
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
|
## grid 2
{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } |
{0:~ }|
{0:~ }|
{0:~ }|
## grid 3
{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x }|
{19: }{15:y }|
{19: }{15: }|
{15: }|
]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
else
screen:expect([[
{17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } {17:𐌢̀́̂̃̅̄𐌢̀́̂̃̅̄}{15:x } |
{0:~ }{19: }{15:y }{0: }|
{0:~ }{19: }{15: }{0: }|
{0:~ }{15: }{0: }|
|
]])
end
command('sign unplace 1 buffer=1')
local buf = meths.create_buf(false, true)
meths.win_set_buf(win, buf)
if multigrid then
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
[2:----------------------------------------]|
|
## grid 2
{19: }{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } |
{0:~ }|
{0:~ }|
{0:~ }|
## grid 3
{15: }|
{15: }|
{15: }|
{15: }|
]], float_pos={[3] = {{id = 1001}, "NW", 1, 4, 10, true}}}
else
screen:expect([[
{19: }{20: 1 }{21:^x }|
{19: }{14: 2 }y |
{19: }{14: 3 } {15: } |
{0:~ }{15: }{0: }|
{0:~ }{15: }{0: }|
{0:~ }{15: }{0: }|
|
]])
end
end)
it('can have minimum size', function()
insert("the background text")
local buf = meths.create_buf(false, true)