mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-28 16:29:34 +08:00
Improve mouse support for menu and toggle.
This commit is contained in:
@@ -5,10 +5,8 @@
|
||||
namespace ftxui {
|
||||
|
||||
Element Button::Render() {
|
||||
return text(label) | //
|
||||
border | //
|
||||
(Focused() ? inverted : nothing) | //
|
||||
reflect(box_);
|
||||
auto style = Focused() ? inverted : nothing;
|
||||
return text(label) | border | style | reflect(box_);
|
||||
}
|
||||
|
||||
bool Button::OnEvent(Event event) {
|
||||
|
@@ -107,10 +107,19 @@ Event Event::MouseMiddleDown(std::string input, int x, int y) {
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::CursorReporting(std::string input, int x, int y) {
|
||||
Event event;
|
||||
event.input_ = std::move(input);
|
||||
event.type_ = Type::CursorReporting;
|
||||
event.mouse_ = {x, y};
|
||||
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:
|
||||
|
@@ -6,16 +6,21 @@
|
||||
namespace ftxui {
|
||||
|
||||
Element Menu::Render() {
|
||||
std::vector<Element> elements;
|
||||
bool is_focused = Focused();
|
||||
Elements elements;
|
||||
bool is_menu_focused = Focused();
|
||||
boxes_.resize(entries.size());
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
auto style = (selected != int(i))
|
||||
? normal_style
|
||||
: is_focused ? focused_style : selected_style;
|
||||
auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select;
|
||||
auto icon = (selected != int(i)) ? L" " : L"> ";
|
||||
elements.push_back(text(icon + entries[i]) | style | focused |
|
||||
bool is_focused = (focused == int(i)) && is_menu_focused;
|
||||
bool is_selected = (selected == int(i));
|
||||
|
||||
auto style = is_selected
|
||||
? (is_focused ? selected_focused_style : selected_style)
|
||||
: (is_focused ? focused_style : normal_style);
|
||||
auto focus_management = !is_selected ? nothing
|
||||
: is_menu_focused ? focus
|
||||
: select;
|
||||
auto icon = is_selected ? L"> " : L" ";
|
||||
elements.push_back(text(icon + entries[i]) | style | focus_management |
|
||||
reflect(boxes_[i]));
|
||||
}
|
||||
return vbox(std::move(elements));
|
||||
@@ -41,6 +46,7 @@ bool Menu::OnEvent(Event event) {
|
||||
selected = std::max(0, std::min(int(entries.size()) - 1, selected));
|
||||
|
||||
if (selected != old_selected) {
|
||||
focused = selected;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
@@ -58,10 +64,11 @@ bool Menu::OnMouseEvent(Event event) {
|
||||
if (!boxes_[i].Contain(event.mouse_x(), event.mouse_y()))
|
||||
continue;
|
||||
|
||||
focused = i;
|
||||
if (event.is_mouse_left_down()) {
|
||||
TakeFocus();
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
TakeFocus();
|
||||
on_change();
|
||||
}
|
||||
return true;
|
||||
|
@@ -156,7 +156,9 @@ static const char USE_ALTERNATIVE_SCREEN[] = "\x1B[?1049h";
|
||||
static const char USE_NORMAL_SCREEN[] = "\x1B[?1049l";
|
||||
|
||||
static const char ENABLE_MOUSE[] = "\x1B[?1000;1003;1006;1015h";
|
||||
static const char DISABLE_MOUSE[] = "\x1B[?1000;1003;10006;1015l";
|
||||
static const char DISABLE_MOUSE[] = "\x1B[?1000;1003;1006;1015l";
|
||||
|
||||
static const char REQUEST_CURSOR_LINE[] = "\x1b[6n";
|
||||
|
||||
using SignalHandler = void(int);
|
||||
std::stack<std::function<void()>> on_exit_functions;
|
||||
@@ -304,17 +306,30 @@ void ScreenInteractive::Loop(Component* component) {
|
||||
while (!quit_) {
|
||||
if (!event_receiver_->HasPending()) {
|
||||
std::cout << reset_cursor_position << ResetPosition();
|
||||
static int i = -2;
|
||||
if (i % 30 == 0)
|
||||
std::cout << REQUEST_CURSOR_LINE;
|
||||
++i;
|
||||
Draw(component);
|
||||
std::cout << ToString() << set_cursor_position;
|
||||
Flush();
|
||||
Clear();
|
||||
}
|
||||
|
||||
Event event;
|
||||
if (event_receiver_->Receive(&event)) {
|
||||
if (event.is_mouse())
|
||||
event.MoveMouse(-1, -1);
|
||||
component->OnEvent(event);
|
||||
if (!event_receiver_->Receive(&event))
|
||||
break;
|
||||
|
||||
if (event.is_cursor_reporting()) {
|
||||
cursor_x_ = event.mouse_y();
|
||||
cursor_y_ = event.mouse_x();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.is_mouse())
|
||||
event.MoveMouse(-cursor_x_, -cursor_y_);
|
||||
|
||||
component->OnEvent(event);
|
||||
}
|
||||
|
||||
event_listener.join();
|
||||
|
@@ -85,6 +85,11 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
|
||||
out_->Send(Event::MouseRightMove(std::move(pending_), output.mouse.x,
|
||||
output.mouse.y));
|
||||
break;
|
||||
|
||||
case CURSOR_REPORTING:
|
||||
out_->Send(Event::CursorReporting(std::move(pending_), output.mouse.x,
|
||||
output.mouse.y));
|
||||
break;
|
||||
}
|
||||
pending_.clear();
|
||||
}
|
||||
@@ -179,12 +184,14 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Current() >= ' ' && Current() <= '~') {
|
||||
if (Current() >= ' ' && Current() <= '~' && Current() != '<') {
|
||||
arguments.push_back(argument);
|
||||
argument = 0;
|
||||
switch (Current()) {
|
||||
case 'M':
|
||||
return ParseMouse(std::move(arguments));
|
||||
case 'R':
|
||||
return ParseCursorReporting(std::move(arguments));
|
||||
default:
|
||||
return SPECIAL;
|
||||
}
|
||||
@@ -235,8 +242,18 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse(
|
||||
return Output(MOUSE_UP, arguments[1], arguments[2]);
|
||||
case 67:
|
||||
return Output(MOUSE_MOVE, arguments[1], arguments[2]);
|
||||
|
||||
default:
|
||||
return Output(MOUSE_MOVE, arguments[1], arguments[2]);
|
||||
}
|
||||
return SPECIAL;
|
||||
}
|
||||
|
||||
TerminalInputParser::Output TerminalInputParser::ParseCursorReporting(
|
||||
std::vector<int> arguments) {
|
||||
if (arguments.size() != 2)
|
||||
return SPECIAL;
|
||||
return Output(CURSOR_REPORTING, arguments[0], arguments[1]);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
@@ -32,6 +32,7 @@ class TerminalInputParser {
|
||||
MOUSE_MIDDLE_MOVE,
|
||||
MOUSE_RIGHT_DOWN,
|
||||
MOUSE_RIGHT_MOVE,
|
||||
CURSOR_REPORTING,
|
||||
};
|
||||
|
||||
struct Mouse {
|
||||
@@ -58,6 +59,7 @@ class TerminalInputParser {
|
||||
Output ParseCSI();
|
||||
Output ParseOSC();
|
||||
Output ParseMouse(std::vector<int> arguments);
|
||||
Output ParseCursorReporting(std::vector<int> arguments);
|
||||
|
||||
Sender<Event> out_;
|
||||
int position_ = -1;
|
||||
|
@@ -66,6 +66,25 @@ TEST(Event, EscapeKeyEnoughWait) {
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
TEST(Event, GnomeTerminalMouse) {
|
||||
auto event_receiver = MakeReceiver<Event>();
|
||||
{
|
||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||
parser.Add('\x1B');
|
||||
parser.Add('[');
|
||||
parser.Add('<');
|
||||
parser.Add('1');
|
||||
parser.Add(';');
|
||||
parser.Add('1');
|
||||
parser.Add('M');
|
||||
}
|
||||
|
||||
Event received;
|
||||
EXPECT_TRUE(event_receiver->Receive(&received));
|
||||
EXPECT_TRUE(received.is_mouse());
|
||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@@ -5,22 +5,25 @@
|
||||
namespace ftxui {
|
||||
|
||||
Element Toggle::Render() {
|
||||
bool is_focused = Focused();
|
||||
|
||||
boxes_.resize(entries.size());
|
||||
|
||||
Elements children;
|
||||
bool is_toggle_focused = Focused();
|
||||
boxes_.resize(entries.size());
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
// Separator.
|
||||
if (i != 0)
|
||||
children.push_back(separator());
|
||||
|
||||
// Entry.
|
||||
auto style = (selected != int(i)) ? normal_style
|
||||
: is_focused ? focused_style
|
||||
: selected_style;
|
||||
auto focused = (selected != int(i)) ? nothing : is_focused ? focus : select;
|
||||
children.push_back(text(entries[i]) | style | focused | reflect(boxes_[i]));
|
||||
bool is_focused = (focused == int(i)) && is_toggle_focused;
|
||||
bool is_selected = (selected == int(i));
|
||||
|
||||
auto style = is_selected
|
||||
? (is_focused ? selected_focused_style : selected_style)
|
||||
: (is_focused ? focused_style : normal_style);
|
||||
auto focus_management = !is_selected ? nothing
|
||||
: is_toggle_focused ? focus
|
||||
: select;
|
||||
children.push_back(text(entries[i]) | style | focus_management |
|
||||
reflect(boxes_[i]));
|
||||
}
|
||||
return hbox(std::move(children));
|
||||
}
|
||||
@@ -42,6 +45,7 @@ bool Toggle::OnEvent(Event event) {
|
||||
selected = std::max(0, std::min(int(entries.size()) - 1, selected));
|
||||
|
||||
if (old_selected != selected) {
|
||||
focused = selected;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
@@ -59,10 +63,12 @@ bool Toggle::OnMouseEvent(Event event) {
|
||||
if (!boxes_[i].Contain(event.mouse_x(), event.mouse_y()))
|
||||
continue;
|
||||
|
||||
TakeFocus();
|
||||
focused = i;
|
||||
if (event.is_mouse_left_down()) {
|
||||
TakeFocus();
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
TakeFocus();
|
||||
on_change();
|
||||
}
|
||||
return true;
|
||||
|
Reference in New Issue
Block a user