From 4e75cf9e0e6de5a211ad29b97b9124aeb3e45582 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 15:33:25 +0700 Subject: [PATCH 1/7] We have a basic decorator --- CMakeLists.txt | 1 + examples/component/CMakeLists.txt | 1 + examples/component/selectable_input.cpp | 80 +++++++++++++++++++++++++ include/ftxui/dom/elements.hpp | 2 + src/ftxui/dom/selectable.cpp | 39 ++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 examples/component/selectable_input.cpp create mode 100644 src/ftxui/dom/selectable.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf4a9da..f478249 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ add_library(dom src/ftxui/dom/paragraph.cpp src/ftxui/dom/reflect.cpp src/ftxui/dom/scroll_indicator.cpp + src/ftxui/dom/selectable.cpp src/ftxui/dom/separator.cpp src/ftxui/dom/size.cpp src/ftxui/dom/spinner.cpp diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index 4339215..b55bdc6 100644 --- a/examples/component/CMakeLists.txt +++ b/examples/component/CMakeLists.txt @@ -38,6 +38,7 @@ example(radiobox) example(radiobox_in_frame) example(renderer) example(resizable_split) +example(selectable_input) example(scrollbar) example(slider) example(slider_direction) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp new file mode 100644 index 0000000..e2fc15e --- /dev/null +++ b/examples/component/selectable_input.cpp @@ -0,0 +1,80 @@ +// 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. +#include // for allocator, __shared_ptr_access +#include // for char_traits, operator+, string, basic_string + +#include "ftxui/component/captured_mouse.hpp" // for ftxui +#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical +#include "ftxui/component/component_base.hpp" // for ComponentBase +#include "ftxui/component/component_options.hpp" // for InputOption +#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive +#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border +#include "ftxui/util/ref.hpp" // for Ref + + +int main() { + using namespace ftxui; + + // The data: + std::string first_name; + std::string last_name; + std::string password; + std::string phoneNumber; + // Region selection; + std::string textToCopy; + + auto screen = ScreenInteractive::TerminalOutput(); + + // The basic input components: + Component input_first_name = Input(&first_name, "first name"); + Component input_last_name = Input(&last_name, "last name"); + + // The password input component: + InputOption password_option; + password_option.password = true; + Component input_password = Input(&password, "password", password_option); + + // The phone number input component: + // We are using `CatchEvent` to filter out non-digit characters. + Component input_phone_number = Input(&phoneNumber, "phone number"); + input_phone_number |= CatchEvent([&](Event event) { + return event.is_character() && !std::isdigit(event.character()[0]); + }); + input_phone_number |= CatchEvent([&](Event event) { + return event.is_character() && phoneNumber.size() > 10; + }); + + // The component tree: + auto component = Container::Vertical({ + input_first_name, + input_last_name, + input_password, + input_phone_number, + }); + + // Tweak how the component tree is rendered: + auto renderer = Renderer(component, [&] { + return vbox({ + hbox(text(" First name : "), input_first_name->Render()), + hbox(text(" Last name : ") | selectable(), input_last_name->Render()), + hbox(text(" Password : "), input_password->Render()), + hbox(text(" Phone num : "), input_phone_number->Render()), + separator(), + text("Hello " + first_name + " " + last_name), + text("Your password is " + password), + text("Your phone number is " + phoneNumber), + // text("select_start " + std::to_string(selection.startx) + ";" + std::to_string(selection.starty)), + // text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), + text("textToCopy " + textToCopy) + }) | + border; // | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}) + }); + + // renderer |= CatchEvent([&](Event event) { + + // return selectableCatchEvent(event); + // }); + + screen.Loop(renderer); +} diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index b17a711..325caa9 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -113,6 +113,8 @@ Decorator focusPositionRelative(float x, float y); Element automerge(Element child); Decorator hyperlink(std::string link); Element hyperlink(std::string link, Element child); +Element selectable(Element child); +Decorator selectable(void); // --- Layout is // Horizontal, Vertical or stacked set of elements. diff --git a/src/ftxui/dom/selectable.cpp b/src/ftxui/dom/selectable.cpp new file mode 100644 index 0000000..9f6f868 --- /dev/null +++ b/src/ftxui/dom/selectable.cpp @@ -0,0 +1,39 @@ +#include "ftxui/dom/elements.hpp" // for Element, Decorator +#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator + + + +namespace ftxui { +namespace { + +class Selectable : public NodeDecorator { + public: + explicit Selectable(Element child) + : NodeDecorator(std::move(child)) {} + + private: + void Render(Screen& screen) override { + + for (int y = box_.y_min; y <= box_.y_max; ++y) { + for (int x = box_.x_min; x <= box_.x_max; ++x) { + screen.PixelAt(x, y).inverted = true; + } + } + + NodeDecorator::Render(screen); + } +}; + +} // namespace + + +Element selectable(Element child) { + return std::make_shared(std::move(child)); +} + +Decorator selectable(void) { + return + [](Element child) { return selectable(std::move(child)); }; +} + +} // namespace ftxui From dea2d6408b3b3ecd09b2d90eab833c4aedbe7bbd Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 17:35:21 +0700 Subject: [PATCH 2/7] We can catch mouse events --- examples/component/selectable_input.cpp | 2 +- .../ftxui/component/screen_interactive.hpp | 13 +++++ include/ftxui/dom/elements.hpp | 3 ++ include/ftxui/screen/pixel.hpp | 2 + src/ftxui/component/screen_interactive.cpp | 51 ++++++++++++++++++- src/ftxui/dom/selectable.cpp | 4 +- 6 files changed, 71 insertions(+), 4 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index e2fc15e..fd16b2d 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -59,7 +59,7 @@ int main() { hbox(text(" First name : "), input_first_name->Render()), hbox(text(" Last name : ") | selectable(), input_last_name->Render()), hbox(text(" Password : "), input_password->Render()), - hbox(text(" Phone num : "), input_phone_number->Render()), + hbox(text(" Phone num : "), input_phone_number->Render()) | selectable(), separator(), text("Hello " + first_name + " " + last_name), text("Your password is " + password), diff --git a/include/ftxui/component/screen_interactive.hpp b/include/ftxui/component/screen_interactive.hpp index 6c79913..a01fa95 100644 --- a/include/ftxui/component/screen_interactive.hpp +++ b/include/ftxui/component/screen_interactive.hpp @@ -26,6 +26,15 @@ struct Event; using Component = std::shared_ptr; class ScreenInteractivePrivate; +typedef struct { + + uint16_t startx = 0; + uint16_t endx = 0; + uint16_t starty = 0; + uint16_t endy = 0; + bool changed = false; +} Region; + class ScreenInteractive : public Screen { public: // Constructors: @@ -82,6 +91,8 @@ class ScreenInteractive : public Screen { void RunOnceBlocking(Component component); void HandleTask(Component component, Task& task); + bool selectableCatchEvent(Event event); + std::string getSelection(void); void Draw(Component component); void ResetCursorPosition(); @@ -126,6 +137,8 @@ class ScreenInteractive : public Screen { bool force_handle_ctrl_c_ = true; bool force_handle_ctrl_z_ = true; + Region selectedRegion; + // The style of the cursor to restore on exit. int cursor_reset_shape_ = 1; diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 325caa9..cf79899 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -116,6 +116,9 @@ Element hyperlink(std::string link, Element child); Element selectable(Element child); Decorator selectable(void); +// -- Selection -- +std::string getSelection(void); + // --- Layout is // Horizontal, Vertical or stacked set of elements. Element hbox(Elements); diff --git a/include/ftxui/screen/pixel.hpp b/include/ftxui/screen/pixel.hpp index cbc7cc2..817778b 100644 --- a/include/ftxui/screen/pixel.hpp +++ b/include/ftxui/screen/pixel.hpp @@ -21,6 +21,7 @@ struct Pixel { underlined(false), underlined_double(false), strikethrough(false), + selectable(false), automerge(false) {} // A bit field representing the style: @@ -30,6 +31,7 @@ struct Pixel { bool inverted : 1; bool underlined : 1; bool underlined_double : 1; + bool selectable : 1; bool strikethrough : 1; bool automerge : 1; diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index b5643ed..7b71887 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -35,6 +35,12 @@ #include "ftxui/screen/pixel.hpp" // for Pixel #include "ftxui/screen/terminal.hpp" // for Dimensions, Size + + +#include +#include + + #if defined(_WIN32) #define DEFINE_CONSOLEV2_PROPERTIES #define WIN32_LEAN_AND_MEAN @@ -781,7 +787,15 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { arg.screen_ = this; - const bool handled = component->OnEvent(arg); + bool handled = component->OnEvent(arg); + + if(handled == false) + { + if(selectableCatchEvent(arg)) + { + handled = true; + } + } if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) { RecordSignal(SIGABRT); @@ -824,6 +838,41 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { // clang-format on } +bool ScreenInteractive::selectableCatchEvent(Event event) { + + // std::ofstream MyFile("debug.log", std::ios_base::app); + // MyFile << "Top dog!" << std::endl; + // MyFile.close(); + + if (event.is_mouse()) { + auto& mouse = event.mouse(); + if (mouse.button == Mouse::Left) { + + if (mouse.motion == Mouse::Pressed) { + selectedRegion.startx = mouse.x; + selectedRegion.starty = mouse.y; + selectedRegion.endx = mouse.x; + selectedRegion.endy = mouse.y; + selectedRegion.changed = true; + } else if (mouse.motion == Mouse::Released) { + selectedRegion.endx = mouse.x; + selectedRegion.endy = mouse.y; + selectedRegion.changed = true; + } else if (mouse.motion == Mouse::Moved) { + selectedRegion.endx = mouse.x; + selectedRegion.endy = mouse.y; + selectedRegion.changed = true; + } + } + } + + return false; +} + +std::string ScreenInteractive::getSelection(void) { + return "Selection"; +} + // private // NOLINTNEXTLINE void ScreenInteractive::Draw(Component component) { diff --git a/src/ftxui/dom/selectable.cpp b/src/ftxui/dom/selectable.cpp index 9f6f868..191c5f4 100644 --- a/src/ftxui/dom/selectable.cpp +++ b/src/ftxui/dom/selectable.cpp @@ -1,6 +1,6 @@ #include "ftxui/dom/elements.hpp" // for Element, Decorator #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator - +#include "ftxui/component/event.hpp" // for Event namespace ftxui { @@ -16,7 +16,7 @@ class Selectable : public NodeDecorator { for (int y = box_.y_min; y <= box_.y_max; ++y) { for (int x = box_.x_min; x <= box_.x_max; ++x) { - screen.PixelAt(x, y).inverted = true; + screen.PixelAt(x, y).selectable = true; } } From e4a63318adaf6c793270132b45d214fbd75691bd Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 17:40:48 +0700 Subject: [PATCH 3/7] We can act on the screen --- examples/component/selectable_input.cpp | 3 +++ include/ftxui/component/screen_interactive.hpp | 5 +++-- include/ftxui/dom/elements.hpp | 3 --- src/ftxui/component/screen_interactive.cpp | 18 ++++++++++++++---- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index fd16b2d..c256482 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -55,6 +55,9 @@ int main() { // Tweak how the component tree is rendered: auto renderer = Renderer(component, [&] { + + textToCopy = screen.getSelection(); + return vbox({ hbox(text(" First name : "), input_first_name->Render()), hbox(text(" Last name : ") | selectable(), input_last_name->Render()), diff --git a/include/ftxui/component/screen_interactive.hpp b/include/ftxui/component/screen_interactive.hpp index a01fa95..e6d1560 100644 --- a/include/ftxui/component/screen_interactive.hpp +++ b/include/ftxui/component/screen_interactive.hpp @@ -32,7 +32,6 @@ typedef struct { uint16_t endx = 0; uint16_t starty = 0; uint16_t endy = 0; - bool changed = false; } Region; class ScreenInteractive : public Screen { @@ -77,6 +76,8 @@ class ScreenInteractive : public Screen { void ForceHandleCtrlC(bool force); void ForceHandleCtrlZ(bool force); + std::string getSelection(void); + private: void ExitNow(); @@ -92,7 +93,7 @@ class ScreenInteractive : public Screen { void HandleTask(Component component, Task& task); bool selectableCatchEvent(Event event); - std::string getSelection(void); + void refreshSelection(void); void Draw(Component component); void ResetCursorPosition(); diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index cf79899..325caa9 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -116,9 +116,6 @@ Element hyperlink(std::string link, Element child); Element selectable(Element child); Decorator selectable(void); -// -- Selection -- -std::string getSelection(void); - // --- Layout is // Horizontal, Vertical or stacked set of elements. Element hbox(Elements); diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 7b71887..75acc0a 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -853,15 +853,15 @@ bool ScreenInteractive::selectableCatchEvent(Event event) { selectedRegion.starty = mouse.y; selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - selectedRegion.changed = true; + refreshSelection(); } else if (mouse.motion == Mouse::Released) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - selectedRegion.changed = true; + refreshSelection(); } else if (mouse.motion == Mouse::Moved) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - selectedRegion.changed = true; + refreshSelection(); } } } @@ -869,8 +869,18 @@ bool ScreenInteractive::selectableCatchEvent(Event event) { return false; } +void ScreenInteractive::refreshSelection(void) { + + for (int y = std::min(selectedRegion.starty, selectedRegion.endy); y <= std::max(selectedRegion.starty, selectedRegion.endy); ++y) { + for (int x = std::min(selectedRegion.startx, selectedRegion.endx); x <= std::max(selectedRegion.startx, selectedRegion.endx)-1; ++x) { + PixelAt(x, y).inverted ^= true; + //selectedText += PixelAt(x, y).character; + } + } +} + std::string ScreenInteractive::getSelection(void) { - return "Selection"; + return "Kikoo"; } // private From 364993464abe4ea36b113c1aff826eac0b1573c1 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 21:13:00 +0700 Subject: [PATCH 4/7] It somewhat works --- examples/component/selectable_input.cpp | 2 +- .../ftxui/component/screen_interactive.hpp | 1 + src/ftxui/component/screen_interactive.cpp | 27 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index c256482..a3dcb23 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -69,7 +69,7 @@ int main() { text("Your phone number is " + phoneNumber), // text("select_start " + std::to_string(selection.startx) + ";" + std::to_string(selection.starty)), // text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), - text("textToCopy " + textToCopy) + text("textToCopy is " + textToCopy) }) | border; // | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}) }); diff --git a/include/ftxui/component/screen_interactive.hpp b/include/ftxui/component/screen_interactive.hpp index e6d1560..faf1e83 100644 --- a/include/ftxui/component/screen_interactive.hpp +++ b/include/ftxui/component/screen_interactive.hpp @@ -139,6 +139,7 @@ class ScreenInteractive : public Screen { bool force_handle_ctrl_z_ = true; Region selectedRegion; + std::string selectedText; // The style of the cursor to restore on exit. int cursor_reset_shape_ = 1; diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 75acc0a..d33b4f1 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -357,7 +357,8 @@ ScreenInteractive::ScreenInteractive(int dimx, bool use_alternative_screen) : Screen(dimx, dimy), dimension_(dimension), - use_alternative_screen_(use_alternative_screen) { + use_alternative_screen_(use_alternative_screen), + selectedText("") { task_receiver_ = MakeReceiver(); } @@ -853,15 +854,15 @@ bool ScreenInteractive::selectableCatchEvent(Event event) { selectedRegion.starty = mouse.y; selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - refreshSelection(); + // refreshSelection(); } else if (mouse.motion == Mouse::Released) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - refreshSelection(); + // refreshSelection(); } else if (mouse.motion == Mouse::Moved) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - refreshSelection(); + // refreshSelection(); } } } @@ -871,16 +872,26 @@ bool ScreenInteractive::selectableCatchEvent(Event event) { void ScreenInteractive::refreshSelection(void) { + selectedText = ""; + for (int y = std::min(selectedRegion.starty, selectedRegion.endy); y <= std::max(selectedRegion.starty, selectedRegion.endy); ++y) { for (int x = std::min(selectedRegion.startx, selectedRegion.endx); x <= std::max(selectedRegion.startx, selectedRegion.endx)-1; ++x) { - PixelAt(x, y).inverted ^= true; - //selectedText += PixelAt(x, y).character; + if(PixelAt(x, y).selectable == true) + { + PixelAt(x, y).inverted ^= true; + selectedText += PixelAt(x, y).character; + } } } } std::string ScreenInteractive::getSelection(void) { - return "Kikoo"; + + // std::ofstream MyFile("debug.log", std::ios_base::app); + // MyFile << "Top dog!" << std::endl; + // MyFile.close(); + + return selectedText; } // private @@ -953,6 +964,8 @@ void ScreenInteractive::Draw(Component component) { Render(*this, document); + refreshSelection(); + // Set cursor position for user using tools to insert CJK characters. { const int dx = dimx_ - 1 - cursor_.x + int(dimx_ != terminal.dimx); From 118055d942f5bd66c85f22d42ee1219cc3410b70 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 21:16:04 +0700 Subject: [PATCH 5/7] Cleanup --- examples/component/selectable_input.cpp | 13 ++----------- src/ftxui/component/screen_interactive.cpp | 11 ----------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index a3dcb23..807ef46 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -56,8 +56,6 @@ int main() { // Tweak how the component tree is rendered: auto renderer = Renderer(component, [&] { - textToCopy = screen.getSelection(); - return vbox({ hbox(text(" First name : "), input_first_name->Render()), hbox(text(" Last name : ") | selectable(), input_last_name->Render()), @@ -67,17 +65,10 @@ int main() { text("Hello " + first_name + " " + last_name), text("Your password is " + password), text("Your phone number is " + phoneNumber), - // text("select_start " + std::to_string(selection.startx) + ";" + std::to_string(selection.starty)), - // text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), - text("textToCopy is " + textToCopy) + text("Selected test is " + screen.getSelection()) }) | - border; // | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}) + border; }); - // renderer |= CatchEvent([&](Event event) { - - // return selectableCatchEvent(event); - // }); - screen.Loop(renderer); } diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index d33b4f1..1d45535 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -841,10 +841,6 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { bool ScreenInteractive::selectableCatchEvent(Event event) { - // std::ofstream MyFile("debug.log", std::ios_base::app); - // MyFile << "Top dog!" << std::endl; - // MyFile.close(); - if (event.is_mouse()) { auto& mouse = event.mouse(); if (mouse.button == Mouse::Left) { @@ -854,15 +850,12 @@ bool ScreenInteractive::selectableCatchEvent(Event event) { selectedRegion.starty = mouse.y; selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - // refreshSelection(); } else if (mouse.motion == Mouse::Released) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - // refreshSelection(); } else if (mouse.motion == Mouse::Moved) { selectedRegion.endx = mouse.x; selectedRegion.endy = mouse.y; - // refreshSelection(); } } } @@ -887,10 +880,6 @@ void ScreenInteractive::refreshSelection(void) { std::string ScreenInteractive::getSelection(void) { - // std::ofstream MyFile("debug.log", std::ios_base::app); - // MyFile << "Top dog!" << std::endl; - // MyFile.close(); - return selectedText; } From f8dd258d6581fa20ada5b4ab8f29677f1ab9c6e4 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Tue, 27 Aug 2024 21:20:19 +0700 Subject: [PATCH 6/7] More cleanup --- src/ftxui/component/screen_interactive.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index 1d45535..e66012b 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -35,12 +35,6 @@ #include "ftxui/screen/pixel.hpp" // for Pixel #include "ftxui/screen/terminal.hpp" // for Dimensions, Size - - -#include -#include - - #if defined(_WIN32) #define DEFINE_CONSOLEV2_PROPERTIES #define WIN32_LEAN_AND_MEAN @@ -839,6 +833,7 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { // clang-format on } +// private bool ScreenInteractive::selectableCatchEvent(Event event) { if (event.is_mouse()) { From 3754136dd6671dceda958341d07b182b9ac78f81 Mon Sep 17 00:00:00 2001 From: ArthurSonzogni Date: Sat, 31 Aug 2024 15:39:42 +0200 Subject: [PATCH 7/7] Reformat + fix pending selection. --- examples/component/selectable_input.cpp | 16 ++-- .../ftxui/component/screen_interactive.hpp | 13 +-- include/ftxui/dom/node.hpp | 6 ++ src/ftxui/component/screen_interactive.cpp | 95 ++++++++++++------- 4 files changed, 79 insertions(+), 51 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index 807ef46..91dd577 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -1,10 +1,8 @@ // 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. -#include // for allocator, __shared_ptr_access #include // for char_traits, operator+, string, basic_string -#include "ftxui/component/captured_mouse.hpp" // for ftxui #include "ftxui/component/component.hpp" // for Input, Renderer, Vertical #include "ftxui/component/component_base.hpp" // for ComponentBase #include "ftxui/component/component_options.hpp" // for InputOption @@ -12,7 +10,6 @@ #include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border #include "ftxui/util/ref.hpp" // for Ref - int main() { using namespace ftxui; @@ -38,10 +35,10 @@ int main() { // The phone number input component: // We are using `CatchEvent` to filter out non-digit characters. Component input_phone_number = Input(&phoneNumber, "phone number"); - input_phone_number |= CatchEvent([&](Event event) { + input_phone_number |= CatchEvent([&](const Event& event) { return event.is_character() && !std::isdigit(event.character()[0]); }); - input_phone_number |= CatchEvent([&](Event event) { + input_phone_number |= CatchEvent([&](const Event& event) { return event.is_character() && phoneNumber.size() > 10; }); @@ -55,17 +52,18 @@ int main() { // Tweak how the component tree is rendered: auto renderer = Renderer(component, [&] { - return vbox({ hbox(text(" First name : "), input_first_name->Render()), - hbox(text(" Last name : ") | selectable(), input_last_name->Render()), + hbox(text(" Last name : ") | selectable(), + input_last_name->Render()), hbox(text(" Password : "), input_password->Render()), - hbox(text(" Phone num : "), input_phone_number->Render()) | selectable(), + hbox(text(" Phone num : "), input_phone_number->Render()) | + selectable(), separator(), text("Hello " + first_name + " " + last_name), text("Your password is " + password), text("Your phone number is " + phoneNumber), - text("Selected test is " + screen.getSelection()) + text("Selected test is " + screen.GetSelection()), }) | border; }); diff --git a/include/ftxui/component/screen_interactive.hpp b/include/ftxui/component/screen_interactive.hpp index faf1e83..49f1837 100644 --- a/include/ftxui/component/screen_interactive.hpp +++ b/include/ftxui/component/screen_interactive.hpp @@ -27,7 +27,6 @@ using Component = std::shared_ptr; class ScreenInteractivePrivate; typedef struct { - uint16_t startx = 0; uint16_t endx = 0; uint16_t starty = 0; @@ -76,7 +75,7 @@ class ScreenInteractive : public Screen { void ForceHandleCtrlC(bool force); void ForceHandleCtrlZ(bool force); - std::string getSelection(void); + std::string GetSelection(); private: void ExitNow(); @@ -92,8 +91,8 @@ class ScreenInteractive : public Screen { void RunOnceBlocking(Component component); void HandleTask(Component component, Task& task); - bool selectableCatchEvent(Event event); - void refreshSelection(void); + bool HandleSelection(Event event); + void RefreshSelection(); void Draw(Component component); void ResetCursorPosition(); @@ -138,8 +137,10 @@ class ScreenInteractive : public Screen { bool force_handle_ctrl_c_ = true; bool force_handle_ctrl_z_ = true; - Region selectedRegion; - std::string selectedText; + bool selection_enabled = false; + CapturedMouse selection_pending; + Region selection_region; + std::string selection_text; // The style of the cursor to restore on exit. int cursor_reset_shape_ = 1; diff --git a/include/ftxui/dom/node.hpp b/include/ftxui/dom/node.hpp index f43157a..5a71512 100644 --- a/include/ftxui/dom/node.hpp +++ b/include/ftxui/dom/node.hpp @@ -52,6 +52,12 @@ class Node { }; virtual void Check(Status* status); + // Selection. + // Propagated from Parents to Children. + virtual void Select(Box selected_area) { + // TODO: Implement this. + } + protected: Elements children_; Requirement requirement_; diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index e66012b..0112f62 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -352,7 +352,7 @@ ScreenInteractive::ScreenInteractive(int dimx, : Screen(dimx, dimy), dimension_(dimension), use_alternative_screen_(use_alternative_screen), - selectedText("") { + selection_text("") { task_receiver_ = MakeReceiver(); } @@ -784,13 +784,7 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { bool handled = component->OnEvent(arg); - if(handled == false) - { - if(selectableCatchEvent(arg)) - { - handled = true; - } - } + handled = handled || HandleSelection(arg); if (arg == Event::CtrlC && (!handled || force_handle_ctrl_c_)) { RecordSignal(SIGABRT); @@ -834,48 +828,77 @@ void ScreenInteractive::HandleTask(Component component, Task& task) { } // private -bool ScreenInteractive::selectableCatchEvent(Event event) { +bool ScreenInteractive::HandleSelection(Event event) { + if (!event.is_mouse()) { + return false; + } - if (event.is_mouse()) { - auto& mouse = event.mouse(); - if (mouse.button == Mouse::Left) { + auto& mouse = event.mouse(); + if (mouse.button != Mouse::Left) { + return false; + } - if (mouse.motion == Mouse::Pressed) { - selectedRegion.startx = mouse.x; - selectedRegion.starty = mouse.y; - selectedRegion.endx = mouse.x; - selectedRegion.endy = mouse.y; - } else if (mouse.motion == Mouse::Released) { - selectedRegion.endx = mouse.x; - selectedRegion.endy = mouse.y; - } else if (mouse.motion == Mouse::Moved) { - selectedRegion.endx = mouse.x; - selectedRegion.endy = mouse.y; - } + if (mouse.motion == Mouse::Pressed) { + selection_pending = CaptureMouse(); + if (!selection_pending) { + return false; } + selection_enabled = true; + selection_region.startx = mouse.x; + selection_region.starty = mouse.y; + selection_region.endx = mouse.x; + selection_region.endy = mouse.y; + return true; + } + + if (!selection_pending) { + return false; + } + + if (mouse.motion == Mouse::Moved) { + selection_region.endx = mouse.x; + selection_region.endy = mouse.y; + return true; + } + + if (mouse.motion == Mouse::Released) { + selection_region.endx = mouse.x; + selection_region.endy = mouse.y; + selection_pending = nullptr; + + if (selection_region.startx == selection_region.endx && + selection_region.starty == selection_region.endy) { + selection_enabled = false; + return true; + } + + return true; } return false; } -void ScreenInteractive::refreshSelection(void) { +void ScreenInteractive::RefreshSelection() { + if (!selection_enabled) { + return; + } + selection_text = ""; - selectedText = ""; - - for (int y = std::min(selectedRegion.starty, selectedRegion.endy); y <= std::max(selectedRegion.starty, selectedRegion.endy); ++y) { - for (int x = std::min(selectedRegion.startx, selectedRegion.endx); x <= std::max(selectedRegion.startx, selectedRegion.endx)-1; ++x) { - if(PixelAt(x, y).selectable == true) - { + for (int y = std::min(selection_region.starty, selection_region.endy); + y <= std::max(selection_region.starty, selection_region.endy); ++y) { + for (int x = std::min(selection_region.startx, selection_region.endx); + x <= std::max(selection_region.startx, selection_region.endx) - 1; + ++x) { + if (PixelAt(x, y).selectable == true) { PixelAt(x, y).inverted ^= true; - selectedText += PixelAt(x, y).character; + selection_text += PixelAt(x, y).character; } } } } -std::string ScreenInteractive::getSelection(void) { - - return selectedText; +std::string ScreenInteractive::GetSelection() { + return selection_text; } // private @@ -948,7 +971,7 @@ void ScreenInteractive::Draw(Component component) { Render(*this, document); - refreshSelection(); + RefreshSelection(); // Set cursor position for user using tools to insert CJK characters. {