diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2019-01-01 12:46:47 +0100 |
---|---|---|
committer | Björn Linse <bjorn.linse@gmail.com> | 2019-01-20 10:32:05 +0100 |
commit | a2be9c7218d7fb431191cd6146fba61bcd1e193e (patch) | |
tree | 6a18ff845b028572d6aa1f35900b571821a2082d | |
parent | 8a5c68f6959f8d315adb126ff49fd1970e7e75be (diff) | |
download | rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.gz rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.tar.bz2 rneovim-a2be9c7218d7fb431191cd6146fba61bcd1e193e.zip |
ui: multigrid mouse support
-rw-r--r-- | runtime/doc/ui.txt | 1 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 96 | ||||
-rw-r--r-- | src/nvim/edit.c | 5 | ||||
-rw-r--r-- | src/nvim/eval.c | 3 | ||||
-rw-r--r-- | src/nvim/getchar.c | 11 | ||||
-rw-r--r-- | src/nvim/globals.h | 5 | ||||
-rw-r--r-- | src/nvim/mouse.c | 71 | ||||
-rw-r--r-- | src/nvim/normal.c | 5 | ||||
-rw-r--r-- | src/nvim/os/input.c | 109 | ||||
-rw-r--r-- | src/nvim/terminal.c | 4 | ||||
-rw-r--r-- | src/nvim/ui.c | 1 | ||||
-rw-r--r-- | src/nvim/window.c | 18 | ||||
-rw-r--r-- | test/functional/ui/mouse_spec.lua | 92 | ||||
-rw-r--r-- | test/functional/ui/multigrid_spec.lua | 337 |
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) |