feat(float): support callback for mouse event

This commit is contained in:
zeertzjq 2024-09-27 15:58:42 +08:00
parent 9b8907d905
commit ae4d5b83f4
9 changed files with 120 additions and 18 deletions

View File

@ -3198,10 +3198,15 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_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.
events. May be a boolean or a Lua callback. Defaults to
`focusable` value.
• If false, mouse events pass through this window.
• If true, mouse events interact with this window
normally.
• If a Lua callback, mouse events interact with this
window, but the callback is called in place of the
default handling. The default handling will be used
again if callback returns `true`.
• external: GUI should display the window as an external
top-level window. Currently accepts no other positioning
configuration together with this.

View File

@ -1770,9 +1770,12 @@ function vim.api.nvim_open_term(buffer, opts) end
--- `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.
--- May be a boolean or a Lua callback. Defaults to `focusable` value.
--- - If false, mouse events pass through this window.
--- - If true, mouse events interact with this window normally.
--- - If a Lua callback, mouse events interact with this window,
--- but the callback is called in place of the default handling.
--- The default handling will be used again if callback returns `true`.
--- - external: GUI should display the window as an external
--- top-level window. Currently accepts no other positioning
--- configuration together with this.

View File

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

View File

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

View File

@ -1,3 +1,4 @@
#include <lauxlib.h>
#include <stdbool.h>
#include <string.h>
@ -23,6 +24,7 @@
#include "nvim/globals.h"
#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@ -132,9 +134,12 @@
/// |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.
/// May be a boolean or a Lua callback. Defaults to `focusable` value.
/// - If false, mouse events pass through this window.
/// - If true, mouse events interact with this window normally.
/// - If a Lua callback, mouse events interact with this window,
/// but the callback is called in place of the default handling.
/// The default handling will be used again if callback returns `true`.
/// - external: GUI should display the window as an external
/// top-level window. Currently accepts no other positioning
/// configuration together with this.
@ -719,7 +724,18 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
PUT_KEY_X(rv, focusable, config->focusable);
PUT_KEY_X(rv, external, config->external);
PUT_KEY_X(rv, hide, config->hide);
PUT_KEY_X(rv, mouse, config->mouse);
switch (config->mouse) {
case kWinMouseIgnore:
PUT_KEY_X(rv, mouse, BOOLEAN_OBJ(false));
break;
case kWinMouseDefault:
PUT_KEY_X(rv, mouse, BOOLEAN_OBJ(true));
break;
case kWinMouseCallback:
PUT_KEY_X(rv, mouse, LUAREF_OBJ(api_new_luaref(config->mouse_cb)));
break;
}
if (wp->w_floating) {
PUT_KEY_X(rv, width, config->width);
@ -1208,11 +1224,22 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
if (HAS_KEY_X(config, focusable)) {
fconfig->focusable = config->focusable;
fconfig->mouse = config->focusable;
if (fconfig->mouse != kWinMouseCallback) {
fconfig->mouse = config->focusable ? kWinMouseDefault : kWinMouseIgnore;
}
}
if (HAS_KEY_X(config, mouse)) {
fconfig->mouse = config->mouse;
if (config->mouse.type == kObjectTypeLuaRef) {
fconfig->mouse = kWinMouseCallback;
fconfig->mouse_cb = config->mouse.data.luaref;
config->mouse.data.luaref = LUA_NOREF;
} else if (config->mouse.type == kObjectTypeBoolean) {
fconfig->mouse = config->mouse.data.boolean ? kWinMouseDefault : kWinMouseIgnore;
fconfig->mouse_cb = LUA_NOREF;
} else {
api_set_error(err, kErrorTypeValidation, "invalid type for 'mouse'");
}
}
if (HAS_KEY_X(config, zindex)) {

View File

@ -1,5 +1,6 @@
#pragma once
#include <lauxlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@ -904,6 +905,12 @@ typedef enum {
kFloatRelativeMouse = 3,
} FloatRelative;
typedef enum {
kWinMouseIgnore = 0,
kWinMouseDefault = 1,
kWinMouseCallback = 2,
} WinMouseEvent;
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
typedef enum {
kWinSplitLeft = 0,
@ -938,7 +945,8 @@ typedef struct {
FloatRelative relative;
bool external;
bool focusable;
bool mouse;
WinMouseEvent mouse;
LuaRef mouse_cb;
WinSplit split;
int zindex;
WinStyle style;
@ -965,7 +973,8 @@ typedef struct {
.row = 0, .col = 0, .anchor = 0, \
.relative = 0, .external = false, \
.focusable = true, \
.mouse = true, \
.mouse = kWinMouseDefault, \
.mouse_cb = LUA_NOREF, \
.split = 0, \
.zindex = kZIndexFloatDefault, \
.style = kWinStyleUnused, \

View File

@ -4,6 +4,8 @@
#include <stdlib.h>
#include <string.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@ -13,6 +15,7 @@
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/window.h"
#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
@ -20,6 +23,7 @@
#include "nvim/grid.h"
#include "nvim/grid_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
@ -222,6 +226,45 @@ static void call_click_def_func(StlClickDefinition *click_defs, int col, int whi
got_click = false;
}
/// When mouse events happen in a window with a mouse callback, invoke it.
///
/// @param button !is_scroll: return value of get_mouse_button()
/// is_scroll: a MSCR_* enum
///
/// @return whether to proceeded with mouse handling.
static bool handle_mouse_cb(bool is_click, bool is_drag, bool is_scroll, int button)
{
static handle_T last_click_win = 0;
int grid = mouse_grid;
int row = mouse_row;
int col = mouse_col;
win_T *wp = NULL;
if (!is_click && !is_scroll && button != MOUSE_RELEASE) { // drag or release
if (last_click_win > 0) {
wp = win_id2wp(last_click_win);
}
} else { // click, scroll or move
wp = mouse_find_win(&grid, &row, &col);
if (is_click) {
last_click_win = wp != NULL ? wp->handle : 0;
}
}
if (wp == NULL || wp->w_config.mouse != kWinMouseCallback) {
return true;
}
Error err = ERROR_INIT;
Object res = nlua_call_ref(wp->w_config.mouse_cb, NULL, (Array)ARRAY_DICT_INIT,
kRetNilBool, NULL, &err);
bool retval = LUARET_TRUTHY(res);
if (ERROR_SET(&err)) {
semsg_multiline("E5108: %s", err.msg);
api_clear_error(&err);
}
return retval;
}
/// Translate window coordinates to buffer position without any side effects.
/// Returns IN_BUFFER and sets "mpos->col" to the column when in buffer text.
/// The column is one for the first column.
@ -371,6 +414,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
break;
}
if (!handle_mouse_cb(is_click, is_drag, false, which_button)) {
return false;
}
if (c == K_MOUSEMOVE) {
// Mouse moved without a button pressed.
return false;
@ -1063,6 +1110,10 @@ void do_mousescroll(cmdarg_T *cap)
/// of the MSCR_ values.
void ins_mousescroll(int dir)
{
if (!handle_mouse_cb(false, false, true, dir)) {
return;
}
cmdarg_T cap;
oparg_T oa;
CLEAR_FIELD(cap);
@ -1572,6 +1623,10 @@ static bool do_mousescroll_horiz(colnr_T leftcol)
/// "cap->arg", which is one of the MSCR_ values.
void nv_mousescroll(cmdarg_T *cap)
{
if (!handle_mouse_cb(false, false, true, cap->arg)) {
return;
}
win_T *const old_curwin = curwin;
if (mouse_row >= 0 && mouse_col >= 0) {
@ -1740,7 +1795,7 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp)
} else if (*gridp > 1) {
win_T *wp = get_win_by_grid_handle(*gridp);
if (wp && wp->w_grid_alloc.chars
&& !(wp->w_floating && !wp->w_config.mouse)) {
&& !(wp->w_floating && wp->w_config.mouse == kWinMouseIgnore)) {
*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);
return wp;

View File

@ -45,6 +45,7 @@
#include "nvim/grid_defs.h"
#include "nvim/hashtab.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
@ -802,6 +803,9 @@ int win_fdccol_count(win_T *wp)
void merge_win_config(WinConfig *dst, const WinConfig src)
FUNC_ATTR_NONNULL_ALL
{
if (dst->mouse_cb != src.mouse_cb) {
api_free_luaref(dst->mouse_cb);
}
if (dst->title_chunks.items != src.title_chunks.items) {
clear_virttext(&dst->title_chunks);
}
@ -857,7 +861,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
String anchor = cstr_as_string(float_anchor_str[c.anchor]);
if (!c.hide) {
ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor,
grid->handle, row, col, c.mouse,
grid->handle, row, col, c.mouse != kWinMouseIgnore,
wp->w_grid_alloc.zindex);
} else {
ui_call_win_hide(wp->w_grid_alloc.handle);
@ -889,7 +893,7 @@ void ui_ext_win_position(win_T *wp, bool validate)
ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col,
wp->w_height_outer, wp->w_width_outer, valid, false);
ui_check_cursor_grid(wp->w_grid_alloc.handle);
wp->w_grid_alloc.mouse_enabled = wp->w_config.mouse;
wp->w_grid_alloc.mouse_enabled = wp->w_config.mouse != kWinMouseIgnore;
if (!valid) {
wp->w_grid_alloc.valid = false;
redraw_later(wp, UPD_NOT_VALID);
@ -4044,7 +4048,7 @@ void win_alloc_aucmd_win(int idx)
fconfig.width = Columns;
fconfig.height = 5;
fconfig.focusable = false;
fconfig.mouse = false;
fconfig.mouse = kWinMouseIgnore;
aucmd_win[idx].auc_win = win_new_float(NULL, true, fconfig, &err);
aucmd_win[idx].auc_win->w_buffer->b_nwindows--;
RESET_BINDING(aucmd_win[idx].auc_win);
@ -5263,9 +5267,8 @@ void win_free(win_T *wp, tabpage_T *tp)
}
}
// free the border text
clear_virttext(&wp->w_config.title_chunks);
clear_virttext(&wp->w_config.footer_chunks);
// free allocated resources in w_config
merge_win_config(&wp->w_config, WIN_CONFIG_INIT);
clear_matches(wp);

View File

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