mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-29 14:45:53 +08:00
Improve mouse support
This commit is contained in:
parent
8037a5fa5f
commit
0b9b6c692a
@ -83,6 +83,7 @@ add_library(component
|
|||||||
include/ftxui/component/event.hpp
|
include/ftxui/component/event.hpp
|
||||||
include/ftxui/component/input.hpp
|
include/ftxui/component/input.hpp
|
||||||
include/ftxui/component/menu.hpp
|
include/ftxui/component/menu.hpp
|
||||||
|
include/ftxui/component/mouse.hpp
|
||||||
include/ftxui/component/radiobox.hpp
|
include/ftxui/component/radiobox.hpp
|
||||||
include/ftxui/component/receiver.hpp
|
include/ftxui/component/receiver.hpp
|
||||||
include/ftxui/component/screen_interactive.hpp
|
include/ftxui/component/screen_interactive.hpp
|
||||||
|
@ -11,6 +11,60 @@
|
|||||||
|
|
||||||
using namespace ftxui;
|
using namespace ftxui;
|
||||||
|
|
||||||
|
std::wstring Stringify(Event event) {
|
||||||
|
std::wstring out;
|
||||||
|
for (auto& it : event.input())
|
||||||
|
out += L" " + std::to_wstring((unsigned int)it);
|
||||||
|
|
||||||
|
out = L"(" + out + L" ) -> ";
|
||||||
|
if (event.is_character()) {
|
||||||
|
out += std::wstring(L"character(") + event.character() + L")";
|
||||||
|
} else if (event.is_mouse()) {
|
||||||
|
out += L"mouse";
|
||||||
|
switch (event.mouse().button) {
|
||||||
|
case Mouse::Left:
|
||||||
|
out += L"_left";
|
||||||
|
break;
|
||||||
|
case Mouse::Middle:
|
||||||
|
out += L"_middle";
|
||||||
|
break;
|
||||||
|
case Mouse::Right:
|
||||||
|
out += L"_right";
|
||||||
|
break;
|
||||||
|
case Mouse::None:
|
||||||
|
out += L"_none";
|
||||||
|
break;
|
||||||
|
case Mouse::WheelUp:
|
||||||
|
out += L"_wheel_up";
|
||||||
|
break;
|
||||||
|
case Mouse::WheelDown:
|
||||||
|
out += L"_wheel_down";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (event.mouse().motion) {
|
||||||
|
case Mouse::Pressed:
|
||||||
|
out += L"_pressed";
|
||||||
|
break;
|
||||||
|
case Mouse::Released:
|
||||||
|
out += L"_released";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (event.mouse().control)
|
||||||
|
out += L"_control";
|
||||||
|
if (event.mouse().shift)
|
||||||
|
out += L"_shift";
|
||||||
|
if (event.mouse().meta)
|
||||||
|
out += L"_meta";
|
||||||
|
|
||||||
|
out += L"(" + //
|
||||||
|
std::to_wstring(event.mouse().x) + L"," +
|
||||||
|
std::to_wstring(event.mouse().y) + L")";
|
||||||
|
} else {
|
||||||
|
out += L"(special)";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
class DrawKey : public Component {
|
class DrawKey : public Component {
|
||||||
public:
|
public:
|
||||||
~DrawKey() override = default;
|
~DrawKey() override = default;
|
||||||
@ -18,49 +72,7 @@ class DrawKey : public Component {
|
|||||||
Element Render() override {
|
Element Render() override {
|
||||||
Elements children;
|
Elements children;
|
||||||
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
|
for (size_t i = std::max(0, (int)keys.size() - 20); i < keys.size(); ++i) {
|
||||||
std::wstring code;
|
children.push_back(text(Stringify(keys[i])));
|
||||||
for (auto& it : keys[i].input())
|
|
||||||
code += L" " + std::to_wstring((unsigned int)it);
|
|
||||||
|
|
||||||
code = L"(" + code + L" ) -> ";
|
|
||||||
if (keys[i].is_character()) {
|
|
||||||
code += std::wstring(L"character(") + keys[i].character() + L")";
|
|
||||||
} else if (keys[i].is_mouse_move()) {
|
|
||||||
code += L"mouse_move(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_up()) {
|
|
||||||
code += L"mouse_up(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_left_down()) {
|
|
||||||
code += L"mouse_left_down(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_left_move()) {
|
|
||||||
code += L"mouse_left_move(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_middle_down()) {
|
|
||||||
code += L"mouse_middle_down(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_middle_move()) {
|
|
||||||
code += L"mouse_middle_move(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_right_down()) {
|
|
||||||
code += L"mouse_right_down(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else if (keys[i].is_mouse_right_move()) {
|
|
||||||
code += L"mouse_right_move(" + //
|
|
||||||
std::to_wstring(keys[i].mouse_x()) + L"," +
|
|
||||||
std::to_wstring(keys[i].mouse_y()) + L")";
|
|
||||||
} else {
|
|
||||||
code += L"(special)";
|
|
||||||
}
|
|
||||||
children.push_back(text(code));
|
|
||||||
}
|
}
|
||||||
return window(text(L"keys"), vbox(std::move(children)));
|
return window(text(L"keys"), vbox(std::move(children)));
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define FTXUI_COMPONENT_EVENT_HPP
|
#define FTXUI_COMPONENT_EVENT_HPP
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <ftxui/component/mouse.hpp>
|
||||||
#include <ftxui/component/receiver.hpp>
|
#include <ftxui/component/receiver.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -28,14 +29,7 @@ struct Event {
|
|||||||
|
|
||||||
static Event Character(std::string);
|
static Event Character(std::string);
|
||||||
static Event Special(std::string);
|
static Event Special(std::string);
|
||||||
static Event MouseMove(std::string, int x, int y);
|
static Event Mouse(std::string, Mouse mouse);
|
||||||
static Event MouseUp(std::string, int x, int y);
|
|
||||||
static Event MouseLeftMove(std::string, int x, int y);
|
|
||||||
static Event MouseLeftDown(std::string, int x, int y);
|
|
||||||
static Event MouseMiddleMove(std::string, int x, int y);
|
|
||||||
static Event MouseMiddleDown(std::string, int x, int y);
|
|
||||||
static Event MouseRightMove(std::string, int x, int y);
|
|
||||||
static Event MouseRightDown(std::string, int x, int y);
|
|
||||||
static Event CursorReporting(std::string, int x, int y);
|
static Event CursorReporting(std::string, int x, int y);
|
||||||
|
|
||||||
// --- Arrow ---
|
// --- Arrow ---
|
||||||
@ -60,51 +54,38 @@ struct Event {
|
|||||||
bool is_character() const { return type_ == Type::Character;}
|
bool is_character() const { return type_ == Type::Character;}
|
||||||
wchar_t character() const { return character_; }
|
wchar_t character() const { return character_; }
|
||||||
|
|
||||||
bool is_mouse() const;
|
bool is_mouse() const { return type_ == Type::Mouse; }
|
||||||
bool is_mouse_left_down() const { return type_ == Type::MouseLeftDown; }
|
struct Mouse& mouse() {
|
||||||
bool is_mouse_left_move() const { return type_ == Type::MouseLeftMove; }
|
return mouse_;
|
||||||
bool is_mouse_middle_down() const { return type_ == Type::MouseMiddleDown; }
|
}
|
||||||
bool is_mouse_middle_move() const { return type_ == Type::MouseMiddleMove; }
|
|
||||||
bool is_mouse_right_down() const { return type_ == Type::MouseRightDown; }
|
|
||||||
bool is_mouse_right_move() const { return type_ == Type::MouseRightMove; }
|
|
||||||
bool is_mouse_up() const { return type_ == Type::MouseUp; }
|
|
||||||
bool is_mouse_move() const { return type_ == Type::MouseMove; }
|
|
||||||
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
|
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
|
||||||
int mouse_x() const { return mouse_.x; }
|
int cursor_x() const { return cursor_.x; }
|
||||||
int mouse_y() const { return mouse_.y; }
|
int cursor_y() const { return cursor_.y; }
|
||||||
|
|
||||||
const std::string& input() const { return input_; }
|
const std::string& input() const { return input_; }
|
||||||
|
|
||||||
bool operator==(const Event& other) const { return input_ == other.input_; }
|
bool operator==(const Event& other) const { return input_ == other.input_; }
|
||||||
|
|
||||||
void MoveMouse(int dx, int dy);
|
|
||||||
|
|
||||||
//--- State section ----------------------------------------------------------
|
//--- State section ----------------------------------------------------------
|
||||||
private:
|
private:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Unknown,
|
Unknown,
|
||||||
Character,
|
Character,
|
||||||
MouseMove,
|
Mouse,
|
||||||
MouseUp,
|
|
||||||
MouseLeftDown,
|
|
||||||
MouseLeftMove,
|
|
||||||
MouseMiddleDown,
|
|
||||||
MouseMiddleMove,
|
|
||||||
MouseRightDown,
|
|
||||||
MouseRightMove,
|
|
||||||
CursorReporting,
|
CursorReporting,
|
||||||
};
|
};
|
||||||
|
Type type_ = Type::Unknown;
|
||||||
|
|
||||||
struct Mouse {
|
struct Cursor {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type_ = Type::Unknown;
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
wchar_t character_ = U'?';
|
wchar_t character_ = U'?';
|
||||||
Mouse mouse_;
|
struct Mouse mouse_;
|
||||||
|
struct Cursor cursor_;
|
||||||
};
|
};
|
||||||
std::string input_;
|
std::string input_;
|
||||||
};
|
};
|
||||||
|
41
include/ftxui/component/mouse.hpp
Normal file
41
include/ftxui/component/mouse.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
namespace ftxui {
|
||||||
|
|
||||||
|
/// @brief A mouse event. It contains the coordinate of the mouse, the button
|
||||||
|
/// pressed and the modifier (shift, ctrl, meta).
|
||||||
|
/// @ingroup component
|
||||||
|
struct Mouse {
|
||||||
|
enum Button {
|
||||||
|
Left = 0,
|
||||||
|
Middle = 1,
|
||||||
|
Right = 2,
|
||||||
|
None = 3,
|
||||||
|
WheelUp = 4,
|
||||||
|
WheelDown = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Motion {
|
||||||
|
Released = 0,
|
||||||
|
Pressed = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Button
|
||||||
|
Button button;
|
||||||
|
|
||||||
|
// Motion
|
||||||
|
Motion motion;
|
||||||
|
|
||||||
|
// Modifiers:
|
||||||
|
bool shift;
|
||||||
|
bool meta;
|
||||||
|
bool control;
|
||||||
|
|
||||||
|
// Coordinates:
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ftxui
|
||||||
|
|
||||||
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
|
// the LICENSE file.
|
@ -10,12 +10,11 @@ Element Button::Render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Button::OnEvent(Event event) {
|
bool Button::OnEvent(Event event) {
|
||||||
if (event.is_mouse() && box_.Contain(event.mouse_x(), event.mouse_y())) {
|
if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||||
if (event.is_mouse_move()) {
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
return true;
|
|
||||||
}
|
if (event.mouse().button == Mouse::Left &&
|
||||||
if (event.is_mouse_left_down()) {
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
on_click();
|
on_click();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,13 @@ bool CheckBox::OnEvent(Event event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CheckBox::OnMouseEvent(Event event) {
|
bool CheckBox::OnMouseEvent(Event event) {
|
||||||
if (!box_.Contain(event.mouse_x(), event.mouse_y()))
|
if (!box_.Contain(event.mouse().x, event.mouse().y))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (event.is_mouse_move()) {
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.is_mouse_left_down()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
state = !state;
|
state = !state;
|
||||||
on_change();
|
on_change();
|
||||||
return true;
|
return true;
|
||||||
|
@ -29,56 +29,11 @@ Event Event::Character(wchar_t c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Event Event::MouseMove(std::string input, int x, int y) {
|
Event Event::Mouse(std::string input, struct Mouse mouse) {
|
||||||
Event event;
|
Event event;
|
||||||
event.input_ = std::move(input);
|
event.input_ = std::move(input);
|
||||||
event.type_ = Type::MouseMove;
|
event.type_ = Type::Mouse;
|
||||||
event.mouse_ = {x, y};
|
event.mouse_ = mouse;
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseUp(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseUp;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseLeftDown(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseLeftDown;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseLeftMove(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseLeftMove;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseRightDown(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseRightDown;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseMiddleMove(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseMiddleMove;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,54 +45,15 @@ Event Event::Special(std::string input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
Event Event::MouseRightMove(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseRightMove;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
Event Event::MouseMiddleDown(std::string input, int x, int y) {
|
|
||||||
Event event;
|
|
||||||
event.input_ = std::move(input);
|
|
||||||
event.type_ = Type::MouseMiddleDown;
|
|
||||||
event.mouse_ = {x, y};
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event Event::CursorReporting(std::string input, int x, int y) {
|
Event Event::CursorReporting(std::string input, int x, int y) {
|
||||||
Event event;
|
Event event;
|
||||||
event.input_ = std::move(input);
|
event.input_ = std::move(input);
|
||||||
event.type_ = Type::CursorReporting;
|
event.type_ = Type::CursorReporting;
|
||||||
event.mouse_ = {x, y};
|
event.cursor_.x = x;
|
||||||
|
event.cursor_.y = y;
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Event::is_mouse() const {
|
|
||||||
switch (type_) {
|
|
||||||
case Type::Unknown:
|
|
||||||
case Type::Character:
|
|
||||||
case Type::CursorReporting:
|
|
||||||
return false;
|
|
||||||
case Type::MouseMove:
|
|
||||||
case Type::MouseUp:
|
|
||||||
case Type::MouseLeftDown:
|
|
||||||
case Type::MouseLeftMove:
|
|
||||||
case Type::MouseMiddleDown:
|
|
||||||
case Type::MouseMiddleMove:
|
|
||||||
case Type::MouseRightDown:
|
|
||||||
case Type::MouseRightMove:
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Event::MoveMouse(int dx, int dy) {
|
|
||||||
mouse_.x += dx;
|
|
||||||
mouse_.y += dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Arrow ---
|
// --- Arrow ---
|
||||||
const Event Event::ArrowLeft = Event::Special("\x1B[D");
|
const Event Event::ArrowLeft = Event::Special("\x1B[D");
|
||||||
const Event Event::ArrowRight = Event::Special("\x1B[C");
|
const Event Event::ArrowRight = Event::Special("\x1B[C");
|
||||||
|
@ -61,12 +61,13 @@ bool Menu::OnEvent(Event event) {
|
|||||||
|
|
||||||
bool Menu::OnMouseEvent(Event event) {
|
bool Menu::OnMouseEvent(Event event) {
|
||||||
for (int i = 0; i < boxes_.size(); ++i) {
|
for (int i = 0; i < boxes_.size(); ++i) {
|
||||||
if (!boxes_[i].Contain(event.mouse_x(), event.mouse_y()))
|
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
focused = i;
|
|
||||||
if (event.is_mouse_left_down()) {
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
|
focused = i;
|
||||||
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Released) {
|
||||||
if (selected != i) {
|
if (selected != i) {
|
||||||
selected = i;
|
selected = i;
|
||||||
on_change();
|
on_change();
|
||||||
|
@ -56,16 +56,14 @@ bool RadioBox::OnEvent(Event event) {
|
|||||||
|
|
||||||
bool RadioBox::OnMouseEvent(Event event) {
|
bool RadioBox::OnMouseEvent(Event event) {
|
||||||
for (int i = 0; i < boxes_.size(); ++i) {
|
for (int i = 0; i < boxes_.size(); ++i) {
|
||||||
if (!boxes_[i].Contain(event.mouse_x(), event.mouse_y()))
|
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (event.is_mouse_move()) {
|
|
||||||
focused = i;
|
focused = i;
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.is_mouse_left_down()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
cursor_position = i;
|
cursor_position = i;
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
if (selected != i) {
|
if (selected != i) {
|
||||||
|
@ -146,19 +146,53 @@ void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char SHOW_CURSOR[] = "\x1B[?25h";
|
const std::string CSI = "\x1b[";
|
||||||
static const char HIDE_CURSOR[] = "\x1B[?25l";
|
|
||||||
|
|
||||||
static const char ENABLE_LINE_WRAP[] = "\x1B[7h";
|
// DEC: Digital Equipment Corporation
|
||||||
static const char DISABLE_LINE_WRAP[] = "\x1B[7l";
|
enum class DECMode {
|
||||||
|
kLineWrap = 7,
|
||||||
|
kMouseX10 = 9,
|
||||||
|
kCursor = 25,
|
||||||
|
kMouseVt200 = 1000,
|
||||||
|
kMouseAnyEvent = 1003,
|
||||||
|
kMouseUtf8 = 1005,
|
||||||
|
kMouseSgrExtMode = 1006,
|
||||||
|
kMouseUrxvtMode = 1015,
|
||||||
|
kMouseSgrPixelsMode = 1016,
|
||||||
|
kAlternateScreen = 1049,
|
||||||
|
};
|
||||||
|
|
||||||
static const char USE_ALTERNATIVE_SCREEN[] = "\x1B[?1049h";
|
// Device Status Report (DSR) {
|
||||||
static const char USE_NORMAL_SCREEN[] = "\x1B[?1049l";
|
enum class DSRMode {
|
||||||
|
kCursor = 6,
|
||||||
|
};
|
||||||
|
|
||||||
static const char ENABLE_MOUSE[] = "\x1B[?1000;1003;1006;1015h";
|
const std::string Serialize(std::vector<DECMode> parameters) {
|
||||||
static const char DISABLE_MOUSE[] = "\x1B[?1000;1003;1006;1015l";
|
bool first = true;
|
||||||
|
std::string out;
|
||||||
|
for (DECMode parameter : parameters) {
|
||||||
|
if (!first)
|
||||||
|
out += ";";
|
||||||
|
out += std::to_string(int(parameter));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
static const char REQUEST_CURSOR_LINE[] = "\x1b[6n";
|
// DEC Private Mode Set (DECSET)
|
||||||
|
const std::string Set(std::vector<DECMode> parameters) {
|
||||||
|
return CSI + "?" + Serialize(parameters) + "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEC Private Mode Reset (DECRST)
|
||||||
|
const std::string Reset(std::vector<DECMode> parameters) {
|
||||||
|
return CSI + "?" + Serialize(parameters) + "l";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device Status Report (DSR)
|
||||||
|
const std::string DeviceStatusReport(DSRMode ps) {
|
||||||
|
return CSI + std::to_string(int(ps)) + "n";
|
||||||
|
}
|
||||||
|
|
||||||
using SignalHandler = void(int);
|
using SignalHandler = void(int);
|
||||||
std::stack<std::function<void()>> on_exit_functions;
|
std::stack<std::function<void()>> on_exit_functions;
|
||||||
@ -279,24 +313,44 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
install_signal_handler(SIGWINCH, OnResize);
|
install_signal_handler(SIGWINCH, OnResize);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Commit state:
|
||||||
|
auto flush = [&] {
|
||||||
|
Flush();
|
||||||
|
on_exit_functions.push([] { Flush(); });
|
||||||
|
};
|
||||||
|
|
||||||
|
auto enable = [&](std::vector<DECMode> parameters) {
|
||||||
|
std::cout << Set(parameters);
|
||||||
|
on_exit_functions.push([=] { std::cout << Reset(parameters); });
|
||||||
|
};
|
||||||
|
|
||||||
|
auto disable = [&](std::vector<DECMode> parameters) {
|
||||||
|
std::cout << Reset(parameters);
|
||||||
|
on_exit_functions.push([=] { std::cout << Set(parameters); });
|
||||||
|
};
|
||||||
|
|
||||||
|
flush();
|
||||||
|
|
||||||
if (use_alternative_screen_) {
|
if (use_alternative_screen_) {
|
||||||
std::cout << USE_ALTERNATIVE_SCREEN;
|
enable({
|
||||||
on_exit_functions.push([] { std::cout << USE_NORMAL_SCREEN; });
|
DECMode::kAlternateScreen,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << ENABLE_MOUSE;
|
// On exit, reset cursor one line after the current drawing.
|
||||||
on_exit_functions.push([] { std::cout << DISABLE_MOUSE; });
|
on_exit_functions.push(
|
||||||
|
[=] { std::cout << reset_cursor_position << std::endl; });
|
||||||
|
|
||||||
// Hide the cursor and show it at exit.
|
disable({
|
||||||
std::cout << HIDE_CURSOR;
|
DECMode::kCursor,
|
||||||
std::cout << DISABLE_LINE_WRAP;
|
DECMode::kLineWrap,
|
||||||
Flush();
|
});
|
||||||
on_exit_functions.push([&] {
|
|
||||||
std::cout << reset_cursor_position;
|
enable({
|
||||||
std::cout << SHOW_CURSOR;
|
//DECMode::kMouseVt200,
|
||||||
std::cout << ENABLE_LINE_WRAP;
|
DECMode::kMouseAnyEvent,
|
||||||
std::cout << std::endl;
|
DECMode::kMouseUtf8,
|
||||||
Flush();
|
DECMode::kMouseSgrExtMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
auto event_listener =
|
auto event_listener =
|
||||||
@ -307,8 +361,8 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
if (!event_receiver_->HasPending()) {
|
if (!event_receiver_->HasPending()) {
|
||||||
std::cout << reset_cursor_position << ResetPosition();
|
std::cout << reset_cursor_position << ResetPosition();
|
||||||
static int i = -2;
|
static int i = -2;
|
||||||
if (i % 30 == 0)
|
if (i % 10 == 0)
|
||||||
std::cout << REQUEST_CURSOR_LINE;
|
std::cout << DeviceStatusReport(DSRMode::kCursor);
|
||||||
++i;
|
++i;
|
||||||
Draw(component);
|
Draw(component);
|
||||||
std::cout << ToString() << set_cursor_position;
|
std::cout << ToString() << set_cursor_position;
|
||||||
@ -321,13 +375,15 @@ void ScreenInteractive::Loop(Component* component) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (event.is_cursor_reporting()) {
|
if (event.is_cursor_reporting()) {
|
||||||
cursor_x_ = event.mouse_y();
|
cursor_x_ = event.cursor_x();
|
||||||
cursor_y_ = event.mouse_x();
|
cursor_y_ = event.cursor_y();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.is_mouse())
|
if (event.is_mouse()) {
|
||||||
event.MoveMouse(-cursor_x_, -cursor_y_);
|
event.mouse().x -= cursor_x_;
|
||||||
|
event.mouse().y -= cursor_y_;
|
||||||
|
}
|
||||||
|
|
||||||
component->OnEvent(event);
|
component->OnEvent(event);
|
||||||
}
|
}
|
||||||
|
@ -36,62 +36,28 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case DROP:
|
case DROP:
|
||||||
break;
|
pending_.clear();
|
||||||
|
return;
|
||||||
|
|
||||||
case CHARACTER:
|
case CHARACTER:
|
||||||
out_->Send(Event::Character(std::move(pending_)));
|
out_->Send(Event::Character(std::move(pending_)));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case SPECIAL:
|
case SPECIAL:
|
||||||
out_->Send(Event::Special(std::move(pending_)));
|
out_->Send(Event::Special(std::move(pending_)));
|
||||||
break;
|
return;
|
||||||
|
|
||||||
case MOUSE_MOVE:
|
case MOUSE:
|
||||||
out_->Send(
|
out_->Send(Event::Mouse(std::move(pending_), output.mouse));
|
||||||
Event::MouseMove(std::move(pending_), output.mouse.x, output.mouse.y));
|
return;
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_UP:
|
|
||||||
out_->Send(
|
|
||||||
Event::MouseUp(std::move(pending_), output.mouse.x, output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_LEFT_DOWN:
|
|
||||||
out_->Send(Event::MouseLeftDown(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_LEFT_MOVE:
|
|
||||||
out_->Send(Event::MouseLeftMove(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_MIDDLE_DOWN:
|
|
||||||
out_->Send(Event::MouseMiddleDown(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_MIDDLE_MOVE:
|
|
||||||
out_->Send(Event::MouseMiddleMove(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_RIGHT_DOWN:
|
|
||||||
out_->Send(Event::MouseRightDown(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MOUSE_RIGHT_MOVE:
|
|
||||||
out_->Send(Event::MouseRightMove(std::move(pending_), output.mouse.x,
|
|
||||||
output.mouse.y));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CURSOR_REPORTING:
|
case CURSOR_REPORTING:
|
||||||
out_->Send(Event::CursorReporting(std::move(pending_), output.mouse.x,
|
out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x,
|
||||||
output.mouse.y));
|
output.cursor.y));
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
pending_.clear();
|
// NOT_REACHED().
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalInputParser::Output TerminalInputParser::Parse() {
|
TerminalInputParser::Output TerminalInputParser::Parse() {
|
||||||
@ -166,12 +132,18 @@ TerminalInputParser::Output TerminalInputParser::ParseDCS() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||||
|
bool altered = false;
|
||||||
int argument;
|
int argument;
|
||||||
std::vector<int> arguments;
|
std::vector<int> arguments;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!Eat())
|
if (!Eat())
|
||||||
return UNCOMPLETED;
|
return UNCOMPLETED;
|
||||||
|
|
||||||
|
if (Current() == '<') {
|
||||||
|
altered = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Current() >= '0' && Current() <= '9') {
|
if (Current() >= '0' && Current() <= '9') {
|
||||||
argument *= 10;
|
argument *= 10;
|
||||||
argument += int(Current() - '0');
|
argument += int(Current() - '0');
|
||||||
@ -189,7 +161,9 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
|||||||
argument = 0;
|
argument = 0;
|
||||||
switch (Current()) {
|
switch (Current()) {
|
||||||
case 'M':
|
case 'M':
|
||||||
return ParseMouse(std::move(arguments));
|
return ParseMouse(altered, true, std::move(arguments));
|
||||||
|
case 'm':
|
||||||
|
return ParseMouse(altered, false, std::move(arguments));
|
||||||
case 'R':
|
case 'R':
|
||||||
return ParseCursorReporting(std::move(arguments));
|
return ParseCursorReporting(std::move(arguments));
|
||||||
default:
|
default:
|
||||||
@ -219,41 +193,34 @@ TerminalInputParser::Output TerminalInputParser::ParseOSC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TerminalInputParser::Output TerminalInputParser::ParseMouse(
|
TerminalInputParser::Output TerminalInputParser::ParseMouse(
|
||||||
|
bool altered,
|
||||||
|
bool pressed,
|
||||||
std::vector<int> arguments) {
|
std::vector<int> arguments) {
|
||||||
if (arguments.size() != 3)
|
if (arguments.size() != 3)
|
||||||
return SPECIAL;
|
return SPECIAL;
|
||||||
switch(arguments[0]) {
|
|
||||||
case 32:
|
|
||||||
return Output(MOUSE_LEFT_DOWN, arguments[1], arguments[2]);
|
|
||||||
case 64:
|
|
||||||
return Output(MOUSE_LEFT_MOVE, arguments[1], arguments[2]);
|
|
||||||
|
|
||||||
case 33:
|
(void)altered;
|
||||||
return Output(MOUSE_MIDDLE_DOWN, arguments[1], arguments[2]);
|
|
||||||
case 65:
|
|
||||||
return Output(MOUSE_MIDDLE_MOVE, arguments[1], arguments[2]);
|
|
||||||
|
|
||||||
case 34:
|
Output output(MOUSE);
|
||||||
return Output(MOUSE_RIGHT_DOWN, arguments[1], arguments[2]);
|
output.mouse.button = Mouse::Button((arguments[0] & 3) + //
|
||||||
case 66:
|
((arguments[0] & 64) >> 4));
|
||||||
return Output(MOUSE_RIGHT_MOVE, arguments[1], arguments[2]);
|
output.mouse.motion = Mouse::Motion(pressed);
|
||||||
|
output.mouse.shift = arguments[0] & 4;
|
||||||
case 35:
|
output.mouse.meta = arguments[0] & 8;
|
||||||
return Output(MOUSE_UP, arguments[1], arguments[2]);
|
output.mouse.control = arguments[0] & 16;
|
||||||
case 67:
|
output.mouse.x = arguments[1];
|
||||||
return Output(MOUSE_MOVE, arguments[1], arguments[2]);
|
output.mouse.y = arguments[2];
|
||||||
|
return output;
|
||||||
default:
|
|
||||||
return Output(MOUSE_MOVE, arguments[1], arguments[2]);
|
|
||||||
}
|
|
||||||
return SPECIAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
|
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
|
||||||
std::vector<int> arguments) {
|
std::vector<int> arguments) {
|
||||||
if (arguments.size() != 2)
|
if (arguments.size() != 2)
|
||||||
return SPECIAL;
|
return SPECIAL;
|
||||||
return Output(CURSOR_REPORTING, arguments[0], arguments[1]);
|
Output output(CURSOR_REPORTING);
|
||||||
|
output.cursor.y = arguments[0];
|
||||||
|
output.cursor.x = arguments[1];
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ftxui
|
} // namespace ftxui
|
||||||
|
@ -24,31 +24,23 @@ class TerminalInputParser {
|
|||||||
DROP,
|
DROP,
|
||||||
CHARACTER,
|
CHARACTER,
|
||||||
SPECIAL,
|
SPECIAL,
|
||||||
MOUSE_UP,
|
MOUSE,
|
||||||
MOUSE_MOVE,
|
|
||||||
MOUSE_LEFT_DOWN,
|
|
||||||
MOUSE_LEFT_MOVE,
|
|
||||||
MOUSE_MIDDLE_DOWN,
|
|
||||||
MOUSE_MIDDLE_MOVE,
|
|
||||||
MOUSE_RIGHT_DOWN,
|
|
||||||
MOUSE_RIGHT_MOVE,
|
|
||||||
CURSOR_REPORTING,
|
CURSOR_REPORTING,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Mouse {
|
struct CursorReporting {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
Mouse(int x, int y) : x(x), y(y) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Output {
|
struct Output {
|
||||||
Type type;
|
Type type;
|
||||||
union {
|
union {
|
||||||
Mouse mouse;
|
Mouse mouse;
|
||||||
|
CursorReporting cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
Output(Type type) : type(type) {}
|
Output(Type type) : type(type) {}
|
||||||
Output(Type type, int x, int y) : type(type), mouse(x, y) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void Send(Output type);
|
void Send(Output type);
|
||||||
@ -58,7 +50,7 @@ class TerminalInputParser {
|
|||||||
Output ParseDCS();
|
Output ParseDCS();
|
||||||
Output ParseCSI();
|
Output ParseCSI();
|
||||||
Output ParseOSC();
|
Output ParseOSC();
|
||||||
Output ParseMouse(std::vector<int> arguments);
|
Output ParseMouse(bool altered, bool pressed, std::vector<int> arguments);
|
||||||
Output ParseCursorReporting(std::vector<int> arguments);
|
Output ParseCursorReporting(std::vector<int> arguments);
|
||||||
|
|
||||||
Sender<Event> out_;
|
Sender<Event> out_;
|
||||||
|
@ -66,22 +66,81 @@ TEST(Event, EscapeKeyEnoughWait) {
|
|||||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Event, GnomeTerminalMouse) {
|
TEST(Event, MouseLeftClick) {
|
||||||
auto event_receiver = MakeReceiver<Event>();
|
auto event_receiver = MakeReceiver<Event>();
|
||||||
{
|
{
|
||||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||||
parser.Add('\x1B');
|
parser.Add('\x1B');
|
||||||
parser.Add('[');
|
parser.Add('[');
|
||||||
parser.Add('<');
|
parser.Add('3');
|
||||||
parser.Add('1');
|
parser.Add('2');
|
||||||
parser.Add(';');
|
parser.Add(';');
|
||||||
parser.Add('1');
|
parser.Add('1');
|
||||||
|
parser.Add('2');
|
||||||
|
parser.Add(';');
|
||||||
|
parser.Add('4');
|
||||||
|
parser.Add('2');
|
||||||
parser.Add('M');
|
parser.Add('M');
|
||||||
}
|
}
|
||||||
|
|
||||||
Event received;
|
Event received;
|
||||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||||
EXPECT_TRUE(received.is_mouse());
|
EXPECT_TRUE(received.is_mouse());
|
||||||
|
EXPECT_EQ(Mouse::Left, received.mouse().button);
|
||||||
|
EXPECT_EQ(12, received.mouse().x);
|
||||||
|
EXPECT_EQ(42, received.mouse().y);
|
||||||
|
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Event, MouseMiddleClick) {
|
||||||
|
auto event_receiver = MakeReceiver<Event>();
|
||||||
|
{
|
||||||
|
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||||
|
parser.Add('\x1B');
|
||||||
|
parser.Add('[');
|
||||||
|
parser.Add('3');
|
||||||
|
parser.Add('3');
|
||||||
|
parser.Add(';');
|
||||||
|
parser.Add('1');
|
||||||
|
parser.Add('2');
|
||||||
|
parser.Add(';');
|
||||||
|
parser.Add('4');
|
||||||
|
parser.Add('2');
|
||||||
|
parser.Add('M');
|
||||||
|
}
|
||||||
|
|
||||||
|
Event received;
|
||||||
|
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||||
|
EXPECT_TRUE(received.is_mouse());
|
||||||
|
EXPECT_EQ(Mouse::Middle, received.mouse().button);
|
||||||
|
EXPECT_EQ(12, received.mouse().x);
|
||||||
|
EXPECT_EQ(42, received.mouse().y);
|
||||||
|
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Event, MouseRightClick) {
|
||||||
|
auto event_receiver = MakeReceiver<Event>();
|
||||||
|
{
|
||||||
|
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||||
|
parser.Add('\x1B');
|
||||||
|
parser.Add('[');
|
||||||
|
parser.Add('3');
|
||||||
|
parser.Add('4');
|
||||||
|
parser.Add(';');
|
||||||
|
parser.Add('1');
|
||||||
|
parser.Add('2');
|
||||||
|
parser.Add(';');
|
||||||
|
parser.Add('4');
|
||||||
|
parser.Add('2');
|
||||||
|
parser.Add('M');
|
||||||
|
}
|
||||||
|
|
||||||
|
Event received;
|
||||||
|
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||||
|
EXPECT_TRUE(received.is_mouse());
|
||||||
|
EXPECT_EQ(Mouse::Right, received.mouse().button);
|
||||||
|
EXPECT_EQ(12, received.mouse().x);
|
||||||
|
EXPECT_EQ(42, received.mouse().y);
|
||||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +60,13 @@ bool Toggle::OnEvent(Event event) {
|
|||||||
|
|
||||||
bool Toggle::OnMouseEvent(Event event) {
|
bool Toggle::OnMouseEvent(Event event) {
|
||||||
for (int i = 0; i < boxes_.size(); ++i) {
|
for (int i = 0; i < boxes_.size(); ++i) {
|
||||||
if (!boxes_[i].Contain(event.mouse_x(), event.mouse_y()))
|
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
focused = i;
|
focused = i;
|
||||||
if (event.is_mouse_left_down()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
if (selected != i) {
|
if (selected != i) {
|
||||||
selected = i;
|
selected = i;
|
||||||
|
Loading…
Reference in New Issue
Block a user