From a27c878a3f2510bbd8daf78229a5e459b42590e8 Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Sun, 25 Apr 2021 16:58:16 +0200 Subject: [PATCH] Mouse support. Fix & verify Webassembly support. There was some undefined behavior to be fixed in the terminal input parser. The behavior of flush seems to have change. The fix was to invert '\0' and std::flush. --- examples/index.html | 14 +++---- include/ftxui/component/input.hpp | 5 +++ src/ftxui/component/input.cpp | 38 ++++++++++++++++--- src/ftxui/component/screen_interactive.cpp | 4 +- src/ftxui/component/terminal_input_parser.cpp | 11 ++++-- src/ftxui/screen/screen.cpp | 2 +- 6 files changed, 55 insertions(+), 19 deletions(-) diff --git a/examples/index.html b/examples/index.html index fde820a..461adf2 100644 --- a/examples/index.html +++ b/examples/index.html @@ -10,7 +10,7 @@

FTXUI WebAssembly Example

- FTXUI is a single + FTXUI is a simple C++ library for terminal user interface.

@@ -69,10 +69,8 @@ ]; const url_search_params = new URLSearchParams(window.location.search); - const example_index = url_search_params.get("id") || 16; - const example = example_list[example_index]; - - var select = document.getElementById("selectExample"); + const example = url_search_params.get("file") || "./dom/color_gallery.js" + const select = document.getElementById("selectExample"); for(var i = 0; i < example_list.length; i++) { var opt = example_list[i]; @@ -81,9 +79,10 @@ el.value = opt; select.appendChild(el); } - select.selectedIndex = example_index; + select.selectedIndex = example_list.findIndex(path => path == example) || 0; select.addEventListener("change", () => { - location.href = (location.href).split('?')[0] + "?id=" + select.selectedIndex; + location.href = (location.href).split('?')[0] + "?file=" + + example_list[select.selectedIndex]; }); let stdin_buffer = []; @@ -94,6 +93,7 @@ stdout_buffer = []; let stdout = code => { if (code == 0) { + console.log(code); term.write(new Uint8Array(stdout_buffer)); stdout_buffer = []; } else { diff --git a/include/ftxui/component/input.hpp b/include/ftxui/component/input.hpp index 9b92b83..a55bd30 100644 --- a/include/ftxui/component/input.hpp +++ b/include/ftxui/component/input.hpp @@ -27,6 +27,11 @@ class Input : public Component { // Component implementation. Element Render() override; bool OnEvent(Event) override; + + private: + bool OnMouseEvent(Event); + Box input_box_; + Box cursor_box_; }; } // namespace ftxui diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp index 7a76055..9ca7bcd 100644 --- a/src/ftxui/component/input.cpp +++ b/src/ftxui/component/input.cpp @@ -15,14 +15,15 @@ Element Input::Render() { // Placeholder. if (content.size() == 0) { if (is_focused) - return text(placeholder) | focus | dim | inverted | main_decorator; + return text(placeholder) | focus | dim | inverted | main_decorator | + reflect(input_box_); else - return text(placeholder) | dim | main_decorator; + return text(placeholder) | dim | main_decorator | reflect(input_box_); } // Not focused. if (!is_focused) - return text(content) | main_decorator; + return text(content) | main_decorator | reflect(input_box_); std::wstring part_before_cursor = content.substr(0, cursor_position); std::wstring part_at_cursor = cursor_position < (int)content.size() @@ -37,13 +38,18 @@ Element Input::Render() { return hbox( text(part_before_cursor), - text(part_at_cursor) | underlined | focused, + text(part_at_cursor) | underlined | focused | reflect(cursor_box_), text(part_after_cursor) - ) | flex | inverted | frame | main_decorator; - // clang-format off + ) | flex | inverted | frame | main_decorator | reflect(input_box_); + // clang-format on } + bool Input::OnEvent(Event event) { cursor_position = std::max(0, std::min(content.size(), cursor_position)); + + if (event.is_mouse()) + return OnMouseEvent(event); + std::wstring c; // Backspace. @@ -95,6 +101,26 @@ bool Input::OnEvent(Event event) { return false; } +bool Input::OnMouseEvent(Event event) { + if (!input_box_.Contain(event.mouse().x, event.mouse().y)) + return false; + + TakeFocus(); + + if (event.mouse().button == Mouse::Left && + event.mouse().motion == Mouse::Pressed) { + int new_cursor_position = + cursor_position + event.mouse().x - cursor_box_.x_min; + new_cursor_position = + std::max(0, std::min(content.size(), new_cursor_position)); + if (cursor_position != new_cursor_position) { + cursor_position = new_cursor_position; + on_change(); + } + } + return true; +} + } // namespace ftxui // Copyright 2020 Arthur Sonzogni. All rights reserved. diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index d95af0d..385271a 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -40,7 +40,7 @@ namespace { void Flush() { // Emscripten doesn't implement flush. We interpret zero as flush. - std::cout << std::flush << (char)0; + std::cout << '\0' << std::flush; } constexpr int timeout_milliseconds = 20; @@ -353,6 +353,8 @@ void ScreenInteractive::Loop(Component* component) { DECMode::kMouseSgrExtMode, }); + flush(); + auto event_listener = std::thread(&EventListener, &quit_, event_receiver_->MakeSender()); diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp index 0864d74..b86d583 100644 --- a/src/ftxui/component/terminal_input_parser.cpp +++ b/src/ftxui/component/terminal_input_parser.cpp @@ -41,19 +41,23 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) { case CHARACTER: out_->Send(Event::Character(std::move(pending_))); + pending_.clear(); return; case SPECIAL: out_->Send(Event::Special(std::move(pending_))); + pending_.clear(); return; case MOUSE: out_->Send(Event::Mouse(std::move(pending_), output.mouse)); + pending_.clear(); return; case CURSOR_REPORTING: out_->Send(Event::CursorReporting(std::move(pending_), output.cursor.x, output.cursor.y)); + pending_.clear(); return; } // NOT_REACHED(). @@ -133,7 +137,7 @@ TerminalInputParser::Output TerminalInputParser::ParseDCS() { TerminalInputParser::Output TerminalInputParser::ParseCSI() { bool altered = false; - int argument; + int argument = 0; std::vector arguments; while (true) { if (!Eat()) @@ -205,9 +209,8 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse( output.mouse.button = Mouse::Button((arguments[0] & 3) + // ((arguments[0] & 64) >> 4)); output.mouse.motion = Mouse::Motion(pressed); - output.mouse.shift = arguments[0] & 4; - output.mouse.meta = arguments[0] & 8; - output.mouse.control = arguments[0] & 16; + output.mouse.shift = bool(arguments[0] & 4); + output.mouse.meta = bool(arguments[0] & 8); output.mouse.x = arguments[1]; output.mouse.y = arguments[2]; return output; diff --git a/src/ftxui/screen/screen.cpp b/src/ftxui/screen/screen.cpp index d83e424..3485ee6 100644 --- a/src/ftxui/screen/screen.cpp +++ b/src/ftxui/screen/screen.cpp @@ -184,7 +184,7 @@ std::string Screen::ToString() { } void Screen::Print() { - std::cout << ToString() << std::flush << (char)0; + std::cout << ToString() << '\0' << std::flush; } /// @brief Access a character a given position.