aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2019-01-01 12:46:47 +0100
committerBjörn Linse <bjorn.linse@gmail.com>2019-01-20 10:32:05 +0100
commita2be9c7218d7fb431191cd6146fba61bcd1e193e (patch)
tree6a18ff845b028572d6aa1f35900b571821a2082d
parent8a5c68f6959f8d315adb126ff49fd1970e7e75be (diff)
downloadrneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.gz
rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.bz2
rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.zip
ui: multigrid mouse support
-rw-r--r--runtime/doc/ui.txt1
-rw-r--r--src/nvim/api/vim.c96
-rw-r--r--src/nvim/edit.c5
-rw-r--r--src/nvim/eval.c3
-rw-r--r--src/nvim/getchar.c11
-rw-r--r--src/nvim/globals.h5
-rw-r--r--src/nvim/mouse.c71
-rw-r--r--src/nvim/normal.c5
-rw-r--r--src/nvim/os/input.c109
-rw-r--r--src/nvim/terminal.c4
-rw-r--r--src/nvim/ui.c1
-rw-r--r--src/nvim/window.c18
-rw-r--r--test/functional/ui/mouse_spec.lua92
-rw-r--r--test/functional/ui/multigrid_spec.lua337
14 files changed, 672 insertions, 86 deletions
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 9acd8b2c5a..8235f3a238 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -525,6 +525,7 @@ tabs.
See |ui-linegrid| for grid events.
See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size.
+See |nvim_input_mouse| for sending mouse events to Nvim.
==============================================================================
Popupmenu Events *ui-popupmenu*
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index ce7ef681ef..7f62333d88 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -198,6 +198,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
/// @note |keycodes| like <CR> are translated, so "<" is special.
/// To input a literal "<", send <LT>.
///
+/// For mouse events use |nvim_input_mouse()|. For back-compat reasons
+/// this method supports mouse input as "<LeftMouse><col,row>". This
+/// usage is deprecated since API level 6, use the dedicated method instead.
+///
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
/// requested if the buffer becomes full).
@@ -207,6 +211,98 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys);
}
+/// Send mouse event from GUI
+///
+/// The call is non-blocking. It doesn't wait on any resulting action, but
+/// queues the event to be processed soon by the event loop.
+///
+/// @note Currently this doesn't support "scripting" multiple mouse events
+/// by calling it multiple times in a loop: the intermediate mouse
+/// positions will be ignored. It should be used to implement real-time
+/// mouse input in a GUI. The deprecated pseudokey form
+/// ("<LeftMouse><col,row>") in |nvim_input()| has the same limitiation.
+///
+/// @param button Which mouse button, one of "left", "right", "middle" or
+/// "wheel".
+/// @param action For ordinary buttons, one of "press", "drag" and "release"
+/// For the wheel, use "up", "down", "left" and "right".
+/// @param modifier String of modifiers each represented by a single char.
+/// The same specifiers are used as for a key press, except
+/// that the "-" separator is optional, so "C-A-", "c-a"
+/// and "CA" can all be used to specify Ctrl+Alt+click
+/// @param grid For a client using |ui-multigrid| pass in the grid number.
+/// Other clients should pass in 0 (not 1).
+/// @param row row position of mouse (zero-based, like in redraw events)
+/// @param col column position of mouse (zero-based, like in redraw events)
+void nvim_input_mouse(String button, String action, String modifier,
+ Integer grid, Integer row, Integer col, Error *err)
+ FUNC_API_SINCE(6) FUNC_API_ASYNC
+{
+ if (button.data == NULL || action.data == NULL) {
+ goto error;
+ }
+
+ int code = 0;
+
+ if (strequal(button.data, "left")) {
+ code = KE_LEFTMOUSE;
+ } else if (strequal(button.data, "middle")) {
+ code = KE_MIDDLEMOUSE;
+ } else if (strequal(button.data, "right")) {
+ code = KE_RIGHTMOUSE;
+ } else if (strequal(button.data, "wheel")) {
+ code = KE_MOUSEDOWN;
+ } else {
+ goto error;
+ }
+
+ if (code == KE_MOUSEDOWN) {
+ if (strequal(action.data, "down")) {
+ code = KE_MOUSEUP;
+ } else if (strequal(action.data, "up")) {
+ code = KE_MOUSEDOWN;
+ } else if (strequal(action.data, "left")) {
+ code = KE_MOUSERIGHT;
+ } else if (strequal(action.data, "right")) {
+ code = KE_MOUSELEFT;
+ } else {
+ goto error;
+ }
+ } else {
+ if (strequal(action.data, "press")) {
+ // pass
+ } else if (strequal(action.data, "drag")) {
+ code += KE_LEFTDRAG - KE_LEFTMOUSE;
+ } else if (strequal(action.data, "release")) {
+ code += KE_LEFTRELEASE - KE_LEFTMOUSE;
+ } else {
+ goto error;
+ }
+ }
+
+ int modmask = 0;
+ for (size_t i = 0; i < modifier.size; i++) {
+ char byte = modifier.data[i];
+ if (byte == '-') {
+ continue;
+ }
+ int mod = name_to_mod_mask(byte);
+ if (mod == 0) {
+ api_set_error(err, kErrorTypeValidation,
+ "invalid modifier %c", byte);
+ return;
+ }
+ modmask |= mod;
+ }
+
+ input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col);
+ return;
+
+error:
+ api_set_error(err, kErrorTypeValidation,
+ "invalid button or action");
+}
+
/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
/// the internal representation.
///
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index bb3c0ec196..90507cd0a5 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -7943,11 +7943,10 @@ static void ins_mousescroll(int dir)
pos_T tpos = curwin->w_cursor;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row = mouse_row;
- int col = mouse_col;
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 85b838e443..596eff5b79 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -9521,6 +9521,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (is_mouse_key(n)) {
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
win_T *win;
linenr_T lnum;
win_T *wp;
@@ -9529,7 +9530,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (row >= 0 && col >= 0) {
/* Find the window at the mouse coordinates and compute the
* text position. */
- win = mouse_find_win(&row, &col);
+ win = mouse_find_win(&grid, &row, &col);
if (win == NULL) {
return;
}
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 53e9846c2d..be3e7bb676 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1187,10 +1187,11 @@ void save_typebuf(void)
alloc_typebuf();
}
-static int old_char = -1; /* character put back by vungetc() */
-static int old_mod_mask; /* mod_mask for ungotten character */
-static int old_mouse_row; /* mouse_row related to old_char */
-static int old_mouse_col; /* mouse_col related to old_char */
+static int old_char = -1; // character put back by vungetc()
+static int old_mod_mask; // mod_mask for ungotten character
+static int old_mouse_grid; // mouse_grid related to old_char
+static int old_mouse_row; // mouse_row related to old_char
+static int old_mouse_col; // mouse_col related to old_char
/*
@@ -1391,6 +1392,7 @@ int vgetc(void)
c = old_char;
old_char = -1;
mod_mask = old_mod_mask;
+ mouse_grid = old_mouse_grid;
mouse_row = old_mouse_row;
mouse_col = old_mouse_col;
} else {
@@ -1585,6 +1587,7 @@ vungetc ( /* unget one character (can only be done once!) */
{
old_char = c;
old_mod_mask = mod_mask;
+ old_mouse_grid = mouse_grid;
old_mouse_row = mouse_row;
old_mouse_col = mouse_col;
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index cc39e2c112..8a76ca2bee 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -394,9 +394,8 @@ EXTERN bufref_T au_new_curbuf INIT(= { NULL, 0, 0 });
EXTERN buf_T *au_pending_free_buf INIT(= NULL);
EXTERN win_T *au_pending_free_win INIT(= NULL);
-/*
- * Mouse coordinates, set by check_termcode()
- */
+// Mouse coordinates, set by handle_mouse_event()
+EXTERN int mouse_grid;
EXTERN int mouse_row;
EXTERN int mouse_col;
EXTERN bool mouse_past_bottom INIT(= false); /* mouse below last line */
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 887cbde921..22f5497f1e 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -70,6 +70,7 @@ int jump_to_mouse(int flags,
bool first;
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
int mouse_char;
mouse_past_bottom = false;
@@ -125,20 +126,22 @@ retnomove:
return IN_UNKNOWN;
// find the window where the row is in
- wp = mouse_find_win(&row, &col);
+ wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
dragwin = NULL;
// winpos and height may change in win_enter()!
- if (row >= wp->w_height) { // In (or below) status line
+ if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) {
+ // In (or below) status line
on_status_line = row - wp->w_height + 1;
dragwin = wp;
} else {
on_status_line = 0;
}
- if (col >= wp->w_width) { // In separator line
+ if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) {
+ // In separator line
on_sep_line = col - wp->w_width + 1;
dragwin = wp;
} else {
@@ -160,12 +163,10 @@ retnomove:
&& (wp->w_buffer != curwin->w_buffer
|| (!on_status_line
&& !on_sep_line
- && (
- wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
- col >= wp->w_p_fdc
- + (cmdwin_type == 0 && wp ==
- curwin ? 0 : 1)
- )
+ && (wp->w_p_rl
+ ? col < wp->w_grid.Columns - wp->w_p_fdc
+ : col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin
+ ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode();
redraw_curbuf_later(INVERTED); // delete the inversion
@@ -257,7 +258,7 @@ retnomove:
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
redraw_later(VALID);
row = 0;
- } else if (row >= curwin->w_height) {
+ } else if (row >= curwin->w_grid.Rows) {
count = 0;
for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count; ) {
if (curwin->w_topfill > 0) {
@@ -266,7 +267,7 @@ retnomove:
count += plines(curwin->w_topline);
}
- if (!first && count > row - curwin->w_height + 1) {
+ if (!first && count > row - curwin->w_grid.Rows + 1) {
break;
}
first = false;
@@ -288,7 +289,7 @@ retnomove:
redraw_later(VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- row = curwin->w_height - 1;
+ row = curwin->w_grid.Rows - 1;
} else if (row == 0) {
// When dragging the mouse, while the text has been scrolled up as
// far as it goes, moving the mouse in the top line should scroll
@@ -303,7 +304,7 @@ retnomove:
}
// Check for position outside of the fold column.
- if (curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
+ if (curwin->w_p_rl ? col < curwin->w_grid.Columns - curwin->w_p_fdc :
col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)) {
mouse_char = ' ';
}
@@ -369,8 +370,9 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
int off;
int count;
- if (win->w_p_rl)
- col = win->w_width - 1 - col;
+ if (win->w_p_rl) {
+ col = win->w_grid.Columns - 1 - col;
+ }
lnum = win->w_topline;
@@ -407,7 +409,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
off = win_col_off(win) - win_col_off2(win);
if (col < off)
col = off;
- col += row * (win->w_width - off);
+ col += row * (win->w_grid.Columns - off);
// add skip column (for long wrapping line)
col += win->w_skipcol;
}
@@ -428,11 +430,23 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
return retval;
}
-// Find the window at screen position "*rowp" and "*colp". The positions are
-// updated to become relative to the top-left of the window.
-// Returns NULL when something is wrong.
-win_T *mouse_find_win(int *rowp, int *colp)
+/// Find the window at "grid" position "*rowp" and "*colp". The positions are
+/// updated to become relative to the top-left of the window.
+///
+/// @return NULL when something is wrong.
+win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
{
+ win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp);
+ if (wp_grid) {
+ return wp_grid;
+ }
+
+ // TODO(bfredl): grid zero will have floats displayed on it, and will
+ // be adjusted to float grids.
+ if (*gridp == 0) {
+ *gridp = DEFAULT_GRID_HANDLE;
+ }
+
frame_T *fp;
fp = topframe;
@@ -464,6 +478,19 @@ win_T *mouse_find_win(int *rowp, int *colp)
return NULL;
}
+static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp)
+{
+ if (*grid > 1) {
+ win_T *wp = get_win_by_grid_handle(*grid);
+ if (wp && wp->w_grid.chars) {
+ *rowp = MIN(*rowp, wp->w_grid.Rows-1);
+ *colp = MIN(*colp, wp->w_grid.Columns-1);
+ return wp;
+ }
+ }
+ return NULL;
+}
+
/*
* setmouse() - switch mouse on/off depending on current mode and 'mouse'
*/
@@ -595,7 +622,7 @@ bool mouse_scroll_horiz(int dir)
int step = 6;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- step = curwin->w_width;
+ step = curwin->w_grid.Columns;
}
int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
@@ -647,7 +674,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// Find the offset where scanning should begin.
int offset = wp->w_leftcol;
if (row > 0) {
- offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) -
+ offset += row * (wp->w_grid.Columns - win_col_off(wp) - win_col_off2(wp) -
wp->w_leftcol + wp->w_skipcol);
}
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0dc5f3175d..844e748681 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3997,13 +3997,14 @@ static void nv_mousescroll(cmdarg_T *cap)
win_T *old_curwin = curwin;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row, col;
+ int grid, row, col;
+ grid = mouse_grid;
row = mouse_row;
col = mouse_col;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index f62253cbce..5e2c9ecb36 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -236,6 +236,53 @@ size_t input_enqueue(String keys)
return rv;
}
+static uint8_t check_multiclick(int code, int grid, int row, int col)
+{
+ static int orig_num_clicks = 0;
+ static int orig_mouse_code = 0;
+ static int orig_mouse_grid = 0;
+ static int orig_mouse_col = 0;
+ static int orig_mouse_row = 0;
+ static uint64_t orig_mouse_time = 0; // time of previous mouse click
+
+ if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE
+ || code == KE_MIDDLERELEASE) {
+ return 0;
+ }
+ uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
+
+ // compute the time elapsed since the previous mouse click and
+ // convert p_mouse from ms to ns
+ uint64_t timediff = mouse_time - orig_mouse_time;
+ uint64_t mouset = (uint64_t)p_mouset * 1000000;
+ if (code == orig_mouse_code
+ && timediff < mouset
+ && orig_num_clicks != 4
+ && orig_mouse_grid == grid
+ && orig_mouse_col == col
+ && orig_mouse_row == row) {
+ orig_num_clicks++;
+ } else {
+ orig_num_clicks = 1;
+ }
+ orig_mouse_code = code;
+ orig_mouse_grid = grid;
+ orig_mouse_col = col;
+ orig_mouse_row = row;
+ orig_mouse_time = mouse_time;
+
+ uint8_t modifiers = 0;
+ if (orig_num_clicks == 2) {
+ modifiers |= MOD_MASK_2CLICK;
+ } else if (orig_num_clicks == 3) {
+ modifiers |= MOD_MASK_3CLICK;
+ } else if (orig_num_clicks == 4) {
+ modifiers |= MOD_MASK_4CLICK;
+ }
+ return modifiers;
+}
+
+
// Mouse event handling code(Extract row/col if available and detect multiple
// clicks)
static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
@@ -274,48 +321,16 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
if (row >= Rows) {
row = (int)Rows - 1;
}
+ mouse_grid = 0;
mouse_row = row;
mouse_col = col;
}
*ptr += advance;
}
- static int orig_num_clicks = 0;
- if (mouse_code != KE_LEFTRELEASE && mouse_code != KE_RIGHTRELEASE
- && mouse_code != KE_MIDDLERELEASE) {
- static int orig_mouse_code = 0;
- static int orig_mouse_col = 0;
- static int orig_mouse_row = 0;
- static uint64_t orig_mouse_time = 0; // time of previous mouse click
- uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
-
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
- uint64_t timediff = mouse_time - orig_mouse_time;
- uint64_t mouset = (uint64_t)p_mouset * 1000000;
- if (mouse_code == orig_mouse_code
- && timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_col == mouse_col
- && orig_mouse_row == mouse_row) {
- orig_num_clicks++;
- } else {
- orig_num_clicks = 1;
- }
- orig_mouse_code = mouse_code;
- orig_mouse_col = mouse_col;
- orig_mouse_row = mouse_row;
- orig_mouse_time = mouse_time;
- }
+ uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
+ mouse_row, mouse_col);
- uint8_t modifiers = 0;
- if (orig_num_clicks == 2) {
- modifiers |= MOD_MASK_2CLICK;
- } else if (orig_num_clicks == 3) {
- modifiers |= MOD_MASK_3CLICK;
- } else if (orig_num_clicks == 4) {
- modifiers |= MOD_MASK_4CLICK;
- }
if (modifiers) {
if (buf[1] != KS_MODIFIER) {
@@ -334,6 +349,30 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
return bufsize;
}
+size_t input_enqueue_mouse(int code, uint8_t modifier,
+ int grid, int row, int col)
+{
+ modifier |= check_multiclick(code, grid, row, col);
+ uint8_t buf[7], *p = buf;
+ if (modifier) {
+ p[0] = K_SPECIAL;
+ p[1] = KS_MODIFIER;
+ p[2] = modifier;
+ p += 3;
+ }
+ p[0] = K_SPECIAL;
+ p[1] = KS_EXTRA;
+ p[2] = (uint8_t)code;
+
+ mouse_grid = grid;
+ mouse_row = row;
+ mouse_col = col;
+
+ size_t written = 3 + (size_t)(p-buf);
+ rbuffer_write(input_buffer, (char *)buf, written);
+ return written;
+}
+
/// @return true if the main loop is blocked and waiting for input.
bool input_blocking(void)
{
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 51bf22b31c..eae0f7ca13 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -981,8 +981,8 @@ static void mouse_action(Terminal *term, int button, int row, int col,
// terminal should lose focus
static bool send_mouse_event(Terminal *term, int c)
{
- int row = mouse_row, col = mouse_col;
- win_T *mouse_win = mouse_find_win(&row, &col);
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ win_T *mouse_win = mouse_find_win(&grid, &row, &col);
if (term->forward_mouse && mouse_win->w_buffer->terminal == term) {
// event in the terminal window and mouse events was enabled by the
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 96232ab223..73d45d8192 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -454,5 +454,6 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
wp->w_grid.requested_rows = (int)height;
wp->w_grid.requested_cols = (int)width;
+ win_inner_width_changed(wp);
redraw_win_later(wp, SOME_VALID);
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 3cadfe612a..a2e3825274 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5031,10 +5031,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
}
-/// Set the width of a window.
-void win_new_width(win_T *wp, int width)
+void win_inner_width_changed(win_T *wp)
{
- wp->w_width = width;
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
invalidate_botline_win(wp);
@@ -5042,6 +5040,20 @@ void win_new_width(win_T *wp, int width)
update_topline();
curs_columns(TRUE); /* validate w_wrow */
}
+}
+
+/// Set the width of a window.
+void win_new_width(win_T *wp, int width)
+{
+ wp->w_width = width;
+ // TODO(bfredl): refactor this. There should be some variable
+ // wp->w_inner_width which always contains the final actual width.
+ // Alternatively use wp->w_width for this and introduce wp->w_outer_width
+ // Then use this to fix terminal_resize.
+ if (!ui_is_external(kUIMultigrid) || wp->w_grid.requested_cols == 0) {
+ win_inner_width_changed(wp);
+ }
+
redraw_win_later(wp, NOT_VALID);
wp->w_redr_status = TRUE;
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 8d35df6f48..8ba03953b9 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -440,16 +440,34 @@ describe('ui/mouse/input', function()
local test_click = function(name, click_str, click_num, mouse_button,
modifiers)
- it(name .. ' works', function()
+
+ local function doit(do_click)
eq(1, funcs.has('tablineat'))
- feed(click_str .. '<3,0>')
+ do_click(0,3)
check_reply({0, click_num, mouse_button, modifiers})
- feed(click_str .. '<4,0>')
+ do_click(0,4)
check_reply({})
- feed(click_str .. '<6,0>')
+ do_click(0,6)
check_reply({5, click_num, mouse_button, modifiers, 2})
- feed(click_str .. '<13,0>')
+ do_click(0,13)
check_reply({5, click_num, mouse_button, modifiers, 2})
+ end
+
+ it(name .. ' works (pseudokey)', function()
+ doit(function (row,col)
+ feed(click_str .. '<' .. col .. ',' .. row .. '>')
+ end)
+ end)
+
+ it(name .. ' works (nvim_input_mouse)', function()
+ doit(function (row,col)
+ local buttons = {l='left',m='middle',r='right'}
+ local modstr = (click_num > 1) and tostring(click_num) or ''
+ for char in string.gmatch(modifiers, '%w') do
+ modstr = modstr .. char .. '-' -- - not needed but should be accepted
+ end
+ meths.input_mouse(buttons[mouse_button], 'press', modstr, 0, row, col)
+ end)
end)
end
@@ -617,7 +635,7 @@ describe('ui/mouse/input', function()
feed('<cr>')
end)
- it('mouse whell will target the hovered window', function()
+ local function wheel(use_api)
feed('ggdG')
insert([[
Inserting
@@ -647,7 +665,11 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelDown><0,0>')
+ if use_api then
+ meths.input_mouse('wheel', 'down', '', 0, 0, 0)
+ else
+ feed('<ScrollWheelDown><0,0>')
+ end
screen:expect([[
mouse scrolling {4:│}lines |
^ {4:│}to |
@@ -664,7 +686,11 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelUp><27,0>')
+ if use_api then
+ meths.input_mouse('wheel', 'up', '', 0, 0, 27)
+ else
+ feed('<ScrollWheelUp><27,0>')
+ end
screen:expect([[
mouse scrolling {4:│}text |
^ {4:│}with |
@@ -681,7 +707,12 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
+ if use_api then
+ meths.input_mouse('wheel', 'up', '', 0, 7, 27)
+ meths.input_mouse('wheel', 'up', '', 0, 7, 27)
+ else
+ feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
+ end
screen:expect([[
mouse scrolling {4:│}text |
^ {4:│}with |
@@ -698,9 +729,17 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
+ end
+
+ it('mouse wheel will target the hovered window (pseudokey)', function()
+ wheel(false)
end)
- it('horizontal scrolling', function()
+ it('mouse wheel will target the hovered window (nvim_input_mouse)', function()
+ wheel(true)
+ end)
+
+ it('horizontal scrolling (pseudokey)', function()
command('set sidescroll=0')
feed("<esc>:set nowrap<cr>")
@@ -732,6 +771,39 @@ describe('ui/mouse/input', function()
]])
end)
+ it('horizontal scrolling (nvim_input_mouse)', function()
+ command('set sidescroll=0')
+ feed("<esc>:set nowrap<cr>")
+
+ feed("a <esc>20Ab<esc>")
+ screen:expect([[
+ |
+ |
+ bbbbbbbbbbbbbbb^b |
+ {0:~ }|
+ |
+ ]])
+
+ meths.input_mouse('wheel', 'left', '', 0, 0, 27)
+ screen:expect([[
+ |
+ |
+ n bbbbbbbbbbbbbbbbbbb^b |
+ {0:~ }|
+ |
+ ]])
+
+ feed("^")
+ meths.input_mouse('wheel', 'right', '', 0, 0, 0)
+ screen:expect([[
+ g |
+ |
+ ^t and selection bbbbbbbbb|
+ {0:~ }|
+ |
+ ]])
+ end)
+
describe('on concealed text', function()
-- Helpful for reading the test expectations:
-- :match Error /\^/
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index a5d4e34000..c54d608ec4 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -3,9 +3,11 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local feed, command, insert = helpers.feed, helpers.command, helpers.insert
local eq = helpers.eq
+local meths = helpers.meths
+local wait = helpers.wait
-describe('multigrid screen', function()
+describe('ext_multigrid', function()
local screen
before_each(function()
@@ -1521,4 +1523,337 @@ describe('multigrid screen', function()
{1:~ }|
]])
end)
+
+ it('supports mouse', function()
+ insert('some text\nto be clicked')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicke^d |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 2, 0, 5)
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some ^text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed(':new<cr>')
+ insert('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo')
+
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, sed do eiusm^o |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 2, 1, 6)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, sed do eiusmo |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 3, 1, 4)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, ^sed do eiusmo |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ screen:try_resize_grid(3, 80, 2)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, ^sed do eiusmo |
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 3, 0, 64)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ^eiusmo |
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1,6, 20)
+ -- TODO(bfredl): "batching" input_mouse is formally not supported yet.
+ -- Normally it should work fine in async context when nvim is not blocked,
+ -- but add a wait be sure.
+ wait()
+ meths.input_mouse('left', 'drag', '', 1, 4, 20)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ^eiusmo |
+ {1:~ }|
+ ]])
+
+ feed('<c-w><c-w><c-w>v')
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] [+] }{12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo |
+ {1:~ }|
+ ## grid 4
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1,8, 26)
+ wait()
+ meths.input_mouse('left', 'drag', '', 1, 6, 30)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ {11:[No Name] [+] }{12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo |
+ {1:~ }|
+ ## grid 4
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
end)