feat(float): allow enabling mouse for non-focusable window (#30844)

Problem:  Cannot allow mouse interaction for non-focusable float window.
Solution: Add a "mouse" field to float window config.
This commit is contained in:
zeertzjq
2024-10-20 22:18:26 +08:00
committed by GitHub
parent dff684fdb3
commit 9b8907d905
16 changed files with 70 additions and 21 deletions

View File

@ -3195,7 +3195,13 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
be fractional. be fractional.
• focusable: Enable focus by user actions (wincmds, mouse • focusable: Enable focus by user actions (wincmds, mouse
events). Defaults to true. Non-focusable windows can be events). Defaults to true. Non-focusable windows can be
entered by |nvim_set_current_win()|. entered by |nvim_set_current_win()|, or, when the `mouse`
field is set to true, by mouse events.
• mouse: Specify how this window interacts with mouse
events. Defaults to `focusable` value.
• If false, mouse events pass through this window.
• If true, mouse events interact with this window
normally.
• external: GUI should display the window as an external • external: GUI should display the window as an external
top-level window. Currently accepts no other positioning top-level window. Currently accepts no other positioning
configuration together with this. configuration together with this.

View File

@ -40,6 +40,8 @@ API
This is not expected to break clients because there are no known clients This is not expected to break clients because there are no known clients
that actually use the `return_type` field or the parameter type names that actually use the `return_type` field or the parameter type names
reported by |--api-info| or |nvim_get_api_info()|. reported by |--api-info| or |nvim_get_api_info()|.
• |nvim_open_win()| supports a `mouse` field that allows configuring mouse
interaction with the window separately from `focusable` field.
• Renamed `nvim__id_dictionary` (unsupported/experimental API) to • Renamed `nvim__id_dictionary` (unsupported/experimental API) to
`nvim__id_dict`. `nvim__id_dict`.

View File

@ -610,11 +610,12 @@ tabs.
size). If the window was previously hidden, it should now be shown size). If the window was previously hidden, it should now be shown
again. again.
["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable, zindex] ~ ["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, mouse_enabled, zindex] ~
Display or reconfigure floating window `win`. The window should be Display or reconfigure floating window `win`. The window should be
displayed above another grid `anchor_grid` at the specified position displayed above another grid `anchor_grid` at the specified position
`anchor_row` and `anchor_col`. For the meaning of `anchor` and more `anchor_row` and `anchor_col`. For the meaning of `anchor` and more details
details of positioning, see |nvim_open_win()|. of positioning, see |nvim_open_win()|. `mouse_enabled` is true if the
window can receive mouse events.
["win_external_pos", grid, win] ~ ["win_external_pos", grid, win] ~
Display or reconfigure external window `win`. The window should be Display or reconfigure external window `win`. The window should be

View File

@ -1767,7 +1767,12 @@ function vim.api.nvim_open_term(buffer, opts) end
--- fractional. --- fractional.
--- - focusable: Enable focus by user actions (wincmds, mouse events). --- - focusable: Enable focus by user actions (wincmds, mouse events).
--- Defaults to true. Non-focusable windows can be entered by --- Defaults to true. Non-focusable windows can be entered by
--- `nvim_set_current_win()`. --- `nvim_set_current_win()`, or, when the `mouse` field is set to true,
--- by mouse events.
--- - mouse: Specify how this window interacts with mouse events.
--- Defaults to `focusable` value.
--- - If false, mouse events pass through this window.
--- - If true, mouse events interact with this window normally.
--- - external: GUI should display the window as an external --- - external: GUI should display the window as an external
--- top-level window. Currently accepts no other positioning --- top-level window. Currently accepts no other positioning
--- configuration together with this. --- configuration together with this.

View File

@ -295,6 +295,7 @@ error('Cannot require a meta file')
--- @field bufpos? any[] --- @field bufpos? any[]
--- @field external? boolean --- @field external? boolean
--- @field focusable? boolean --- @field focusable? boolean
--- @field mouse? boolean
--- @field vertical? boolean --- @field vertical? boolean
--- @field zindex? integer --- @field zindex? integer
--- @field border? any --- @field border? any

View File

@ -119,6 +119,7 @@ typedef struct {
Array bufpos; Array bufpos;
Boolean external; Boolean external;
Boolean focusable; Boolean focusable;
Boolean mouse;
Boolean vertical; Boolean vertical;
Integer zindex; Integer zindex;
Object border; Object border;

View File

@ -102,7 +102,7 @@ void win_pos(Integer grid, Window win, Integer startrow, Integer startcol, Integ
Integer height) Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row, void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, Float anchor_row,
Float anchor_col, Boolean focusable, Integer zindex) Float anchor_col, Boolean mouse_enabled, Integer zindex)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_external_pos(Integer grid, Window win) void win_external_pos(Integer grid, Window win)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;

View File

@ -129,7 +129,12 @@
/// fractional. /// fractional.
/// - focusable: Enable focus by user actions (wincmds, mouse events). /// - focusable: Enable focus by user actions (wincmds, mouse events).
/// Defaults to true. Non-focusable windows can be entered by /// Defaults to true. Non-focusable windows can be entered by
/// |nvim_set_current_win()|. /// |nvim_set_current_win()|, or, when the `mouse` field is set to true,
/// by mouse events.
/// - mouse: Specify how this window interacts with mouse events.
/// Defaults to `focusable` value.
/// - If false, mouse events pass through this window.
/// - If true, mouse events interact with this window normally.
/// - external: GUI should display the window as an external /// - external: GUI should display the window as an external
/// top-level window. Currently accepts no other positioning /// top-level window. Currently accepts no other positioning
/// configuration together with this. /// configuration together with this.
@ -714,6 +719,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
PUT_KEY_X(rv, focusable, config->focusable); PUT_KEY_X(rv, focusable, config->focusable);
PUT_KEY_X(rv, external, config->external); PUT_KEY_X(rv, external, config->external);
PUT_KEY_X(rv, hide, config->hide); PUT_KEY_X(rv, hide, config->hide);
PUT_KEY_X(rv, mouse, config->mouse);
if (wp->w_floating) { if (wp->w_floating) {
PUT_KEY_X(rv, width, config->width); PUT_KEY_X(rv, width, config->width);
@ -1202,6 +1208,11 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
if (HAS_KEY_X(config, focusable)) { if (HAS_KEY_X(config, focusable)) {
fconfig->focusable = config->focusable; fconfig->focusable = config->focusable;
fconfig->mouse = config->focusable;
}
if (HAS_KEY_X(config, mouse)) {
fconfig->mouse = config->mouse;
} }
if (HAS_KEY_X(config, zindex)) { if (HAS_KEY_X(config, zindex)) {

View File

@ -938,6 +938,7 @@ typedef struct {
FloatRelative relative; FloatRelative relative;
bool external; bool external;
bool focusable; bool focusable;
bool mouse;
WinSplit split; WinSplit split;
int zindex; int zindex;
WinStyle style; WinStyle style;
@ -964,6 +965,7 @@ typedef struct {
.row = 0, .col = 0, .anchor = 0, \ .row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \ .relative = 0, .external = false, \
.focusable = true, \ .focusable = true, \
.mouse = true, \
.split = 0, \ .split = 0, \
.zindex = kZIndexFloatDefault, \ .zindex = kZIndexFloatDefault, \
.style = kWinStyleUnused, \ .style = kWinStyleUnused, \

View File

@ -80,8 +80,8 @@ struct ScreenGrid {
// whether the compositor should blend the grid with the background grid // whether the compositor should blend the grid with the background grid
bool blending; bool blending;
// whether the grid can be focused with mouse clicks. // whether the grid interacts with mouse events.
bool focusable; bool mouse_enabled;
// z-index: the order in the stack of grids. // z-index: the order in the stack of grids.
int zindex; int zindex;

View File

@ -199,7 +199,7 @@ void msg_grid_validate(void)
ui_call_grid_resize(msg_grid.handle, msg_grid.cols, msg_grid.rows); ui_call_grid_resize(msg_grid.handle, msg_grid.cols, msg_grid.rows);
msg_scrolled_at_flush = msg_scrolled; msg_scrolled_at_flush = msg_scrolled;
msg_grid.focusable = false; msg_grid.mouse_enabled = false;
msg_grid_adj.target = &msg_grid; msg_grid_adj.target = &msg_grid;
} else if (!should_alloc && msg_grid.chars) { } else if (!should_alloc && msg_grid.chars) {
ui_comp_remove_grid(&msg_grid); ui_comp_remove_grid(&msg_grid);

View File

@ -1740,7 +1740,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
} else if (*gridp > 1) { } else if (*gridp > 1) {
win_T *wp = get_win_by_grid_handle(*gridp); win_T *wp = get_win_by_grid_handle(*gridp);
if (wp && wp->w_grid_alloc.chars if (wp && wp->w_grid_alloc.chars
&& !(wp->w_floating && !wp->w_config.focusable)) { && !(wp->w_floating && !wp->w_config.mouse)) {
*rowp = MIN(*rowp - wp->w_grid.row_offset, wp->w_grid.rows - 1); *rowp = MIN(*rowp - wp->w_grid.row_offset, wp->w_grid.rows - 1);
*colp = MIN(*colp - wp->w_grid.col_offset, wp->w_grid.cols - 1); *colp = MIN(*colp - wp->w_grid.col_offset, wp->w_grid.cols - 1);
return wp; return wp;

View File

@ -275,7 +275,7 @@ ScreenGrid *ui_comp_mouse_focus(int row, int col)
{ {
for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) { for (ssize_t i = (ssize_t)kv_size(layers) - 1; i > 0; i--) {
ScreenGrid *grid = kv_A(layers, i); ScreenGrid *grid = kv_A(layers, i);
if (grid->focusable if (grid->mouse_enabled
&& row >= grid->comp_row && row < grid->comp_row + grid->rows && row >= grid->comp_row && row < grid->comp_row + grid->rows
&& col >= grid->comp_col && col < grid->comp_col + grid->cols) { && col >= grid->comp_col && col < grid->comp_col + grid->cols) {
return grid; return grid;

View File

@ -857,7 +857,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
String anchor = cstr_as_string(float_anchor_str[c.anchor]); String anchor = cstr_as_string(float_anchor_str[c.anchor]);
if (!c.hide) { if (!c.hide) {
ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
grid->handle, row, col, c.focusable, grid->handle, row, col, c.mouse,
wp->w_grid_alloc.zindex); wp->w_grid_alloc.zindex);
} else { } else {
ui_call_win_hide(wp->w_grid_alloc.handle); ui_call_win_hide(wp->w_grid_alloc.handle);
@ -889,7 +889,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col, ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
wp->w_height_outer, wp->w_width_outer, valid, false); wp->w_height_outer, wp->w_width_outer, valid, false);
ui_check_cursor_grid(wp->w_grid_alloc.handle); ui_check_cursor_grid(wp->w_grid_alloc.handle);
wp->w_grid_alloc.focusable = wp->w_config.focusable; wp->w_grid_alloc.mouse_enabled = wp->w_config.mouse;
if (!valid) { if (!valid) {
wp->w_grid_alloc.valid = false; wp->w_grid_alloc.valid = false;
redraw_later(wp, UPD_NOT_VALID); redraw_later(wp, UPD_NOT_VALID);
@ -4044,6 +4044,7 @@ void win_alloc_aucmd_win(int idx)
fconfig.width = Columns; fconfig.width = Columns;
fconfig.height = 5; fconfig.height = 5;
fconfig.focusable = false; fconfig.focusable = false;
fconfig.mouse = false;
aucmd_win[idx].auc_win = win_new_float(NULL, true, fconfig, &err); aucmd_win[idx].auc_win = win_new_float(NULL, true, fconfig, &err);
aucmd_win[idx].auc_win->w_buffer->b_nwindows--; aucmd_win[idx].auc_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win[idx].auc_win); RESET_BINDING(aucmd_win[idx].auc_win);

View File

@ -389,6 +389,7 @@ win_T *win_float_create(bool enter, bool new_buf)
config.row = curwin->w_wrow; config.row = curwin->w_wrow;
config.relative = kFloatRelativeEditor; config.relative = kFloatRelativeEditor;
config.focusable = false; config.focusable = false;
config.mouse = false;
config.anchor = 0; // NW config.anchor = 0; // NW
config.noautocmd = true; config.noautocmd = true;
config.hide = true; config.hide = true;

View File

@ -1278,7 +1278,7 @@ describe('float window', function()
it('return their configuration', function() it('return their configuration', function()
local buf = api.nvim_create_buf(false, false) local buf = api.nvim_create_buf(false, false)
local win = api.nvim_open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5, zindex=60}) local win = api.nvim_open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5, zindex=60})
local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20, zindex=60, hide=false} local expected = {anchor='NW', col=5, external=false, focusable=true, mouse=true, height=2, relative='editor', row=3, width=20, zindex=60, hide=false}
eq(expected, api.nvim_win_get_config(win)) eq(expected, api.nvim_win_get_config(win))
eq(true, exec_lua([[ eq(true, exec_lua([[
local expected, win = ... local expected, win = ...
@ -1290,11 +1290,11 @@ describe('float window', function()
end end
return true]], expected, win)) return true]], expected, win))
eq({external=false, focusable=true, hide=false, relative='',split="left",width=40,height=6}, api.nvim_win_get_config(0)) eq({external=false, focusable=true, mouse=true, hide=false, relative='',split="left",width=40,height=6}, api.nvim_win_get_config(0))
if multigrid then if multigrid then
api.nvim_win_set_config(win, {external=true, width=10, height=1}) api.nvim_win_set_config(win, {external=true, width=10, height=1})
eq({external=true,focusable=true,width=10,height=1,relative='',hide=false}, api.nvim_win_get_config(win)) eq({external=true,focusable=true,mouse=true,width=10,height=1,relative='',hide=false}, api.nvim_win_get_config(win))
end end
end) end)
@ -3988,7 +3988,7 @@ describe('float window', function()
]]} ]]}
end end
eq({relative='win', width=12, height=1, bufpos={1,32}, anchor='NW', hide=false, eq({relative='win', width=12, height=1, bufpos={1,32}, anchor='NW', hide=false,
external=false, col=0, row=1, win=firstwin, focusable=true, zindex=50}, api.nvim_win_get_config(win)) external=false, col=0, row=1, win=firstwin, focusable=true, mouse=true, zindex=50}, api.nvim_win_get_config(win))
feed('<c-e>') feed('<c-e>')
if multigrid then if multigrid then
@ -5606,7 +5606,7 @@ describe('float window', function()
end end
end) end)
it("focus by mouse", function() local function test_float_mouse_focus()
if multigrid then if multigrid then
api.nvim_input_mouse('left', 'press', '', 4, 0, 0) api.nvim_input_mouse('left', 'press', '', 4, 0, 0)
screen:expect{grid=[[ screen:expect{grid=[[
@ -5660,10 +5660,18 @@ describe('float window', function()
| |
]]) ]])
end end
end
it("focus by mouse (focusable=true)", function()
test_float_mouse_focus()
end) end)
it("focus by mouse (focusable=false)", function() it("focus by mouse (focusable=false, mouse=true)", function()
api.nvim_win_set_config(win, {focusable=false}) api.nvim_win_set_config(win, {focusable=false, mouse=true})
test_float_mouse_focus()
end)
local function test_float_mouse_no_focus()
api.nvim_buf_set_lines(0, -1, -1, true, {"a"}) api.nvim_buf_set_lines(0, -1, -1, true, {"a"})
expected_pos[4][6] = false expected_pos[4][6] = false
if multigrid then if multigrid then
@ -5721,6 +5729,16 @@ describe('float window', function()
| |
]]) ]])
end end
end
it("focus by mouse (focusable=false)", function()
api.nvim_win_set_config(win, {focusable=false})
test_float_mouse_no_focus()
end)
it("focus by mouse (focusable=true, mouse=false)", function()
api.nvim_win_set_config(win, {mouse=false})
test_float_mouse_no_focus()
end) end)
it("j", function() it("j", function()