mirror of
https://github.com/neovim/neovim.git
synced 2024-12-25 13:45:15 -07:00
input: Recognize mouse events for abstract_ui
This commit is contained in:
parent
b11ada1aba
commit
40977e78a2
@ -1856,13 +1856,12 @@ do_mouse (
|
|||||||
|
|
||||||
save_cursor = curwin->w_cursor;
|
save_cursor = curwin->w_cursor;
|
||||||
|
|
||||||
/*
|
// When "abstract_ui" is active, always recognize mouse events, otherwise:
|
||||||
* When GUI is active, always recognize mouse events, otherwise:
|
// - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
|
||||||
* - Ignore mouse event in normal mode if 'mouse' doesn't include 'n'.
|
// - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
|
||||||
* - Ignore mouse event in visual mode if 'mouse' doesn't include 'v'.
|
// - For command line and insert mode 'mouse' is checked before calling
|
||||||
* - For command line and insert mode 'mouse' is checked before calling
|
// do_mouse().
|
||||||
* do_mouse().
|
if (!abstract_ui) {
|
||||||
*/
|
|
||||||
if (do_always)
|
if (do_always)
|
||||||
do_always = false;
|
do_always = false;
|
||||||
else {
|
else {
|
||||||
@ -1872,6 +1871,7 @@ do_mouse (
|
|||||||
} else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
|
} else if (State == NORMAL && !mouse_has(MOUSE_NORMAL))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
|
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
|
||||||
|
@ -183,17 +183,19 @@ size_t input_enqueue(String keys)
|
|||||||
char *ptr = keys.data, *end = ptr + keys.size;
|
char *ptr = keys.data, *end = ptr + keys.size;
|
||||||
|
|
||||||
while (rbuffer_available(input_buffer) >= 6 && ptr < end) {
|
while (rbuffer_available(input_buffer) >= 6 && ptr < end) {
|
||||||
int new_size = trans_special((char_u **)&ptr,
|
uint8_t buf[6] = {0};
|
||||||
(char_u *)rbuffer_write_ptr(input_buffer),
|
int new_size = trans_special((uint8_t **)&ptr, buf, false);
|
||||||
false);
|
|
||||||
if (!new_size) {
|
if (!new_size) {
|
||||||
// copy the character unmodified
|
// copy the character unmodified
|
||||||
*rbuffer_write_ptr(input_buffer) = *ptr++;
|
*buf = (uint8_t)*ptr++;
|
||||||
new_size = 1;
|
new_size = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_size = handle_mouse_event(&ptr, buf, new_size);
|
||||||
// TODO(tarruda): Don't produce past unclosed '<' characters, except if
|
// TODO(tarruda): Don't produce past unclosed '<' characters, except if
|
||||||
// there's a lot of characters after the '<'
|
// there's a lot of characters after the '<'
|
||||||
rbuffer_produced(input_buffer, (size_t)new_size);
|
rbuffer_write(input_buffer, (char *)buf, (size_t)new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t rv = (size_t)(ptr - keys.data);
|
size_t rv = (size_t)(ptr - keys.data);
|
||||||
@ -201,6 +203,87 @@ size_t input_enqueue(String keys)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mouse event handling code(Extract row/col if available and detect multiple
|
||||||
|
// clicks)
|
||||||
|
static int handle_mouse_event(char **ptr, uint8_t *buf, int bufsize)
|
||||||
|
{
|
||||||
|
int mouse_code = 0;
|
||||||
|
|
||||||
|
if (bufsize == 3) {
|
||||||
|
mouse_code = buf[2];
|
||||||
|
} else if (bufsize == 6) {
|
||||||
|
// prefixed with K_SPECIAL KS_MODIFIER mod
|
||||||
|
mouse_code = buf[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouse_code < KE_LEFTMOUSE || mouse_code > KE_RIGHTRELEASE) {
|
||||||
|
return bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a <[COL],[ROW]> sequence can follow and will set the mouse_row/mouse_col
|
||||||
|
// global variables. This is ugly but its how the rest of the code expects to
|
||||||
|
// find mouse coordinates, and it would be too expensive to refactor this
|
||||||
|
// now.
|
||||||
|
int col, row, advance;
|
||||||
|
if (sscanf(*ptr, "<%d,%d>%n", &col, &row, &advance)) {
|
||||||
|
if (col >= 0 && row >= 0) {
|
||||||
|
mouse_row = row;
|
||||||
|
mouse_col = col;
|
||||||
|
}
|
||||||
|
*ptr += advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int orig_num_clicks = 0;
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
int 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) {
|
||||||
|
// no modifiers in the buffer yet, shift the bytes 3 positions
|
||||||
|
memcpy(buf + 3, buf, 3);
|
||||||
|
// add the modifier sequence
|
||||||
|
buf[0] = K_SPECIAL;
|
||||||
|
buf[1] = KS_MODIFIER;
|
||||||
|
buf[2] = (uint8_t)modifiers;
|
||||||
|
bufsize += 3;
|
||||||
|
} else {
|
||||||
|
buf[2] |= (uint8_t)modifiers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
static bool input_poll(int ms)
|
static bool input_poll(int ms)
|
||||||
{
|
{
|
||||||
if (do_profiling == PROF_YES && ms) {
|
if (do_profiling == PROF_YES && ms) {
|
||||||
|
157
test/functional/ui/mouse_spec.lua
Normal file
157
test/functional/ui/mouse_spec.lua
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
local helpers = require('test.functional.helpers')
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
local clear, feed, nvim = helpers.clear, helpers.feed, helpers.nvim
|
||||||
|
|
||||||
|
describe('Mouse input', function()
|
||||||
|
local screen, hlgroup_colors
|
||||||
|
|
||||||
|
setup(function()
|
||||||
|
hlgroup_colors = {
|
||||||
|
Visual = nvim('name_to_color', 'LightGrey'),
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
nvim('set_option', 'mouse', 'a')
|
||||||
|
-- set mouset to very high value to ensure that even in valgrind/travis,
|
||||||
|
-- nvim will still pick multiple clicks
|
||||||
|
nvim('set_option', 'mouset', 5000)
|
||||||
|
screen = Screen.new(25, 5)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {background = hlgroup_colors.Visual}
|
||||||
|
})
|
||||||
|
feed('itesting<cr>mouse<cr>support and selection<esc>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mouse |
|
||||||
|
support and selectio^ |
|
||||||
|
~ |
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
screen:detach()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('left click moves cursor', function()
|
||||||
|
feed('<LeftMouse><2,1>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mo^se |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
screen:expect([[
|
||||||
|
^esting |
|
||||||
|
mouse |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('left drag changes visual selection', function()
|
||||||
|
-- drag events must be preceded by a click
|
||||||
|
feed('<LeftMouse><2,1>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mo^se |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
feed('<LeftDrag><4,1>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mo{1:us}^ |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL -- |
|
||||||
|
]])
|
||||||
|
feed('<LeftDrag><2,2>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mo{1:use } |
|
||||||
|
{1:su}^port and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL -- |
|
||||||
|
]])
|
||||||
|
feed('<LeftDrag><0,0>')
|
||||||
|
screen:expect([[
|
||||||
|
^{1:esting } |
|
||||||
|
{1:mou}se |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL -- |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('two clicks will select the word and enter VISUAL', function()
|
||||||
|
feed('<LeftMouse><2,2><LeftMouse><2,2>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mouse |
|
||||||
|
{1:suppor}^ and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL -- |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('three clicks will select the line and enter VISUAL LINE', function()
|
||||||
|
feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mouse |
|
||||||
|
{1:su}^{1:port and selection } |
|
||||||
|
~ |
|
||||||
|
-- VISUAL LINE -- |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('four clicks will enter VISUAL BLOCK', function()
|
||||||
|
feed('<LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2><LeftMouse><2,2>')
|
||||||
|
screen:expect([[
|
||||||
|
testing |
|
||||||
|
mouse |
|
||||||
|
su^port and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL BLOCK -- |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('right click extends visual selection to the clicked location', function()
|
||||||
|
feed('<LeftMouse><0,0>')
|
||||||
|
screen:expect([[
|
||||||
|
^esting |
|
||||||
|
mouse |
|
||||||
|
support and selection |
|
||||||
|
~ |
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
feed('<RightMouse><2,2>')
|
||||||
|
screen:expect([[
|
||||||
|
{1:testing } |
|
||||||
|
{1:mouse } |
|
||||||
|
{1:su}^port and selection |
|
||||||
|
~ |
|
||||||
|
-- VISUAL -- |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('ctrl + left click will search for a tag', function()
|
||||||
|
feed('<C-LeftMouse><0,0>')
|
||||||
|
screen:expect([[
|
||||||
|
E433: No tags file |
|
||||||
|
E426: tag not found: test|
|
||||||
|
ing |
|
||||||
|
Press ENTER or type comma|
|
||||||
|
nd to continue^ |
|
||||||
|
]])
|
||||||
|
feed('<cr>')
|
||||||
|
end)
|
||||||
|
end)
|
@ -147,17 +147,21 @@ function Screen:expect(expected, attr_ids)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Screen:_wait(check, timeout)
|
function Screen:_wait(check, timeout)
|
||||||
local err
|
local err, checked = false
|
||||||
local function notification_cb(method, args)
|
local function notification_cb(method, args)
|
||||||
assert(method == 'redraw')
|
assert(method == 'redraw')
|
||||||
self:_redraw(args)
|
self:_redraw(args)
|
||||||
err = check()
|
err = check()
|
||||||
|
checked = true
|
||||||
if not err then
|
if not err then
|
||||||
stop()
|
stop()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
run(nil, notification_cb, nil, timeout or 5000)
|
run(nil, notification_cb, nil, timeout or 5000)
|
||||||
|
if not checked then
|
||||||
|
err = check()
|
||||||
|
end
|
||||||
if err then
|
if err then
|
||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user