Refactor mouse press/release handling to support 3 buttons

Change button to be an additional parameter instead of having separate
events for left/right buttons.

Fixes #3471
This commit is contained in:
Maxime Coste 2020-06-28 19:48:55 +10:00
parent fc3e5ea419
commit d3374e7e5f
6 changed files with 103 additions and 78 deletions

View File

@ -59,11 +59,8 @@ The requests that the json ui can interpret on stdin are:
* keys(String key1, String key2...): keystrokes * keys(String key1, String key2...): keystrokes
* resize(int rows, int columns): notify ui resize * resize(int rows, int columns): notify ui resize
* scroll(int amount): scroll by given line amount * scroll(int amount): scroll by given line amount
* mouse(String type, int line, int column): mouse event. line and column relate to * mouse_move(int line, int column): line and column relate to the cursor position.
the cursor position. type can be: * mouse_press(String button, int line, int column): line and column relate to
- 'move' cursor position, button can be 'left', 'middle' or 'right'
- 'press_left' * mouse_release(String button, int line, int column): same.
- 'press_right'
- 'release_left'
- 'release_right'
* menu_select(int index): explicit select of given menu entry * menu_select(int index): explicit select of given menu entry

View File

@ -92,10 +92,13 @@ struct MouseHandler
Buffer& buffer = context.buffer(); Buffer& buffer = context.buffer();
BufferCoord cursor; BufferCoord cursor;
auto& selections = context.selections(); auto& selections = context.selections();
constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift; constexpr auto modifiers = Key::Modifiers::Control | Key::Modifiers::Alt | Key::Modifiers::Shift | Key::Modifiers::MouseButtonMask;
switch ((key.modifiers & ~modifiers).value) switch ((key.modifiers & ~modifiers).value)
{ {
case Key::Modifiers::MousePressRight: case Key::Modifiers::MousePress:
switch (key.mouse_button())
{
case Key::MouseButton::Right:
m_dragging = false; m_dragging = false;
cursor = context.window().buffer_coord(key.coord()); cursor = context.window().buffer_coord(key.coord());
if (key.modifiers & Key::Modifiers::Control) if (key.modifiers & Key::Modifiers::Control)
@ -105,7 +108,7 @@ struct MouseHandler
selections.sort_and_merge_overlapping(); selections.sort_and_merge_overlapping();
return true; return true;
case Key::Modifiers::MousePressLeft: case Key::MouseButton::Left:
m_dragging = true; m_dragging = true;
m_anchor = context.window().buffer_coord(key.coord()); m_anchor = context.window().buffer_coord(key.coord());
if (not (key.modifiers & Key::Modifiers::Control)) if (not (key.modifiers & Key::Modifiers::Control))
@ -119,8 +122,10 @@ struct MouseHandler
} }
return true; return true;
case Key::Modifiers::MouseReleaseLeft: default: return true;
case Key::Modifiers::MouseReleaseRight: }
case Key::Modifiers::MouseRelease:
if (not m_dragging) if (not m_dragging)
return true; return true;
m_dragging = false; m_dragging = false;

View File

@ -248,31 +248,30 @@ void JsonUI::eval_json(const Value& json)
m_on_key(key); m_on_key(key);
} }
} }
else if (method == "mouse") else if (method == "mouse_move")
{ {
if (params.size() != 3) if (params.size() != 2)
throw invalid_rpc_request("mouse type/coordinates not specified"); throw invalid_rpc_request("mouse coordinates not specified");
if (not params[0].is_a<String>()) if (not params[0].is_a<int>() or not params[1].is_a<int>())
throw invalid_rpc_request("mouse type is not a string");
else if (not params[1].is_a<int>() or
not params[2].is_a<int>())
throw invalid_rpc_request("mouse coordinates are not integers"); throw invalid_rpc_request("mouse coordinates are not integers");
const StringView type = params[0].as<String>(); m_on_key({Key::Modifiers::MousePos, encode_coord({params[0].as<int>(), params[1].as<int>()})});
const Codepoint coord = encode_coord({params[1].as<int>(), params[2].as<int>()}); }
if (type == "move") else if (method == "mouse_press" or method == "mouse_release")
m_on_key({Key::Modifiers::MousePos, coord}); {
else if (type == "press_left") if (params.size() != 3)
m_on_key({Key::Modifiers::MousePressLeft, coord}); throw invalid_rpc_request("mouse button/coordinates not specified");
else if (type == "press_right")
m_on_key({Key::Modifiers::MousePressRight, coord}); if (not params[0].is_a<String>())
else if (type == "release_left") throw invalid_rpc_request("mouse button is not a string");
m_on_key({Key::Modifiers::MouseReleaseLeft, coord}); if (not params[1].is_a<int>() or not params[2].is_a<int>())
else if (type == "release_right") throw invalid_rpc_request("mouse coordinates are not integers");
m_on_key({Key::Modifiers::MouseReleaseRight, coord});
else auto event = method == "mouse_press" ? Key::Modifiers::MousePress : Key::Modifiers::MouseRelease;
throw invalid_rpc_request(format("invalid mouse event type: {}", type)); auto button = str_to_button(params[0].as<String>());
m_on_key({event | Key::to_modifier(button), encode_coord({params[1].as<int>(), params[2].as<int>()})});
} }
else if (method == "scroll") else if (method == "scroll")
{ {

View File

@ -157,21 +157,36 @@ KeyList parse_keys(StringView str)
return result; return result;
} }
StringView button_to_str(Key::MouseButton button)
{
switch (button)
{
case Key::MouseButton::Left: return "left";
case Key::MouseButton::Middle: return "middle";
case Key::MouseButton::Right: return "right";
default: kak_assert(false); throw logic_error{};
}
}
Key::MouseButton str_to_button(StringView str)
{
if (str == "left") return Key::MouseButton::Left;
if (str == "middle") return Key::MouseButton::Middle;
if (str == "right") return Key::MouseButton::Right;
throw runtime_error(format("invalid mouse button name {}", str));
}
String key_to_str(Key key) String key_to_str(Key key)
{ {
const auto coord = key.coord() + DisplayCoord{1,1}; const auto coord = key.coord() + DisplayCoord{1,1};
switch (key.modifiers) switch (Key::Modifiers(key.modifiers & ~Key::Modifiers::MouseButtonMask))
{ {
case Key::Modifiers::MousePos: case Key::Modifiers::MousePos:
return format("<mouse:move:{}.{}>", coord.line, coord.column); return format("<mouse:move:{}.{}>", coord.line, coord.column);
case Key::Modifiers::MousePressLeft: case Key::Modifiers::MousePress:
return format("<mouse:press_left:{}.{}>", coord.line, coord.column); return format("<mouse:press:{}:{}.{}>", button_to_str(key.mouse_button()), coord.line, coord.column);
case Key::Modifiers::MousePressRight: case Key::Modifiers::MouseRelease:
return format("<mouse:press_right:{}.{}>", coord.line, coord.column); return format("<mouse:release:{}:{}.{}>", button_to_str(key.mouse_button()), coord.line, coord.column);
case Key::Modifiers::MouseReleaseLeft:
return format("<mouse:release_left:{}.{}>", coord.line, coord.column);
case Key::Modifiers::MouseReleaseRight:
return format("<mouse:release_right:{}.{}>", coord.line, coord.column);
case Key::Modifiers::Scroll: case Key::Modifiers::Scroll:
return format("<scroll:{}>", static_cast<int>(key.key)); return format("<scroll:{}>", static_cast<int>(key.key));
case Key::Modifiers::Resize: case Key::Modifiers::Resize:

View File

@ -14,6 +14,12 @@ namespace Kakoune
struct Key struct Key
{ {
enum class MouseButton
{
Left,
Middle,
Right
};
enum class Modifiers : int enum class Modifiers : int
{ {
None = 0, None = 0,
@ -21,11 +27,11 @@ struct Key
Alt = 1 << 1, Alt = 1 << 1,
Shift = 1 << 2, Shift = 1 << 2,
MousePressLeft = 1 << 3, MousePress = 1 << 3,
MousePressRight = 1 << 4, MouseRelease = 1 << 4,
MouseReleaseLeft = 1 << 5, MousePos = 1 << 5,
MouseReleaseRight = 1 << 6, MouseButtonMask= 0b11 << 6,
MousePos = 1 << 7,
Scroll = 1 << 8, Scroll = 1 << 8,
Resize = 1 << 9, Resize = 1 << 9,
MenuSelect = 1 << 10, MenuSelect = 1 << 10,
@ -82,6 +88,8 @@ struct Key
constexpr bool operator<(Key other) const { return val() < other.val(); } constexpr bool operator<(Key other) const { return val() < other.val(); }
constexpr DisplayCoord coord() const { return {(int)((key & 0xFFFF0000) >> 16), (int)(key & 0x0000FFFF)}; } constexpr DisplayCoord coord() const { return {(int)((key & 0xFFFF0000) >> 16), (int)(key & 0x0000FFFF)}; }
constexpr MouseButton mouse_button() { return MouseButton{((int)modifiers & (int)Modifiers::MouseButtonMask) >> 6}; }
static Modifiers to_modifier(MouseButton button) { return Key::Modifiers{((int)button << 6) & (int)Modifiers::MouseButtonMask}; }
Optional<Codepoint> codepoint() const; Optional<Codepoint> codepoint() const;
}; };
@ -95,6 +103,8 @@ class StringView;
KeyList parse_keys(StringView str); KeyList parse_keys(StringView str);
String key_to_str(Key key); String key_to_str(Key key);
StringView button_to_str(Key::MouseButton button);
Key::MouseButton str_to_button(StringView str);
constexpr Key shift(Key key) constexpr Key shift(Key key)
{ {

View File

@ -19,6 +19,7 @@
#include <csignal> #include <csignal>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <strings.h>
constexpr char control(char c) { return c & 037; } constexpr char control(char c) { return c & 037; }
@ -669,19 +670,19 @@ Optional<Key> NCursesUI::get_next_key()
return mod; return mod;
}; };
auto mouse_button = [this](Key::Modifiers mod, Codepoint coord, bool left, bool release) { auto mouse_button = [this](Key::Modifiers mod, Key::MouseButton button, Codepoint coord, bool release) {
auto mask = left ? 0x1 : 0x2; auto mask = 1 << (int)button;
if (not release) if (not release)
{ {
mod |= (m_mouse_state & mask) ? Key::Modifiers::MousePos : (left ? Key::Modifiers::MousePressLeft : Key::Modifiers::MousePressRight); mod |= (m_mouse_state & mask) ? Key::Modifiers::MousePos : Key::Modifiers::MousePress;
m_mouse_state |= mask; m_mouse_state |= mask;
} }
else else
{ {
mod |= left ? Key::Modifiers::MouseReleaseLeft : Key::Modifiers::MouseReleaseRight; mod |= Key::Modifiers::MouseRelease;
m_mouse_state &= ~mask; m_mouse_state &= ~mask;
} }
return Key{mod, coord}; return Key{mod | Key::to_modifier(button), coord};
}; };
auto mouse_scroll = [this](Key::Modifiers mod, bool down) -> Key { auto mouse_scroll = [this](Key::Modifiers mod, bool down) -> Key {
@ -752,17 +753,15 @@ Optional<Key> NCursesUI::get_next_key()
const int y = (sgr ? params[2] : next_char() - 32) - 1; const int y = (sgr ? params[2] : next_char() - 32) - 1;
auto coord = encode_coord({y - content_line_offset(), x}); auto coord = encode_coord({y - content_line_offset(), x});
Key::Modifiers mod = parse_mask((b >> 2) & 0x7); Key::Modifiers mod = parse_mask((b >> 2) & 0x7);
switch (b & 0x43) switch (auto code = b & 0x43; code)
{ {
case 0: return mouse_button(mod, coord, true, c == 'm'); case 0: case 1: case 2:
case 2: return mouse_button(mod, coord, false, c == 'm'); return mouse_button(mod, Key::MouseButton{code}, coord, c == 'm');
case 3: case 3:
if (sgr) if (sgr)
return {}; return {};
if (m_mouse_state & 0x1) else if (int guess = ffs(m_mouse_state) - 1; 0 <= guess and guess < 3)
return mouse_button(mod, coord, true, true); return mouse_button(mod, Key::MouseButton{guess}, coord, true);
else if (m_mouse_state & 0x2)
return mouse_button(mod, coord, false, true);
break; break;
case 64: return mouse_scroll(mod, false); case 64: return mouse_scroll(mod, false);
case 65: return mouse_scroll(mod, true); case 65: return mouse_scroll(mod, true);