From 73cd1d0190503a5bb2421a52ba371a9430779b2c Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 20:30:35 +0700 Subject: [PATCH 01/14] I can select some columns --- CMakeLists.txt | 1 + examples/component/input.cpp | 51 ++++++++++++++++++++++++++++++++-- include/ftxui/dom/elements.hpp | 2 ++ src/ftxui/dom/selected.cpp | 49 ++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 src/ftxui/dom/selected.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf4a9da..350278e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ add_library(dom src/ftxui/dom/gridbox.cpp src/ftxui/dom/hbox.cpp src/ftxui/dom/inverted.cpp + src/ftxui/dom/selected.cpp src/ftxui/dom/linear_gradient.cpp src/ftxui/dom/node.cpp src/ftxui/dom/node_decorator.cpp diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 9768359..67ef0bc 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -12,6 +12,7 @@ #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; @@ -21,6 +22,14 @@ int main() { std::string password; std::string phoneNumber; + int select_startx = -1; + int select_starty = -1; + int select_endx = -1; + int select_endy = -1; + 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"); @@ -59,10 +68,48 @@ 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(select_startx) + ";" + std::to_string(select_starty)), + text("select_end " + std::to_string(select_endx) + ";" + std::to_string(select_endy)), + text("textToCopy " + textToCopy) }) | - border; + border | selected(select_startx, select_endx); + }); + + + renderer |= CatchEvent([&](Event event) { + if (event.is_mouse()) { + auto& mouse = event.mouse(); + if (mouse.button == Mouse::Left) { + if (mouse.motion == Mouse::Pressed) { + select_startx = mouse.x; + select_starty = mouse.y; + select_endx = mouse.x; + select_endy = mouse.y; + } else if (mouse.motion == Mouse::Released) { + select_endx = mouse.x; + select_endy = mouse.y; + } + else if (mouse.motion == Mouse::Moved) { + select_endx = mouse.x; + select_endy = mouse.y; + } + + screen.PostEvent(Event::Custom); + return true; + } + } + + // if (event == Event::SpecialKey("Ctrl+Shift+C")) { + // textToCopy = "Kikoo!"; + // //clip::set_text(text_to_copy); // Set the clipboard content + + // screen.PostEvent(Event::Custom); + + // return true; + // } + + return false; }); - auto screen = ScreenInteractive::TerminalOutput(); screen.Loop(renderer); } diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index b17a711..1b74f0c 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -96,6 +96,8 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); +Element selected(int &start, int &end, Element); +Decorator selected(int &start, int &end); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp new file mode 100644 index 0000000..f97b9bb --- /dev/null +++ b/src/ftxui/dom/selected.cpp @@ -0,0 +1,49 @@ +// 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 make_shared +#include // for move + +#include "ftxui/dom/elements.hpp" // for Element, inverted +#include "ftxui/dom/node.hpp" // for Node +#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator +#include "ftxui/screen/box.hpp" // for Box +#include "ftxui/screen/screen.hpp" // for Pixel, Screen + +namespace ftxui { + +namespace { +class Selected : public NodeDecorator { + public: + using NodeDecorator::NodeDecorator; + Selected(Element child, int &start, int &end) + : NodeDecorator(std::move(child)), startx_(start), endx_(end) {} + + void Render(Screen& screen) override { + Node::Render(screen); + for (int y = box_.y_min; y <= box_.y_max; ++y) { + for (int x = startx_; x <= endx_; ++x) { + screen.PixelAt(x, y).inverted ^= true; + } + } + } + +private: + int &startx_; + int &endx_; +}; +} // namespace + +/// @brief Add a filter that will invert the foreground and the background +/// colors. +/// @ingroup dom + +Element selected(int &start, int &end, Element child) { + return std::make_shared(std::move(child), start, end); +} + +Decorator selected(int &start, int &end) { + return [&start, &end](Element child) { return selected(start, end, std::move(child)); }; +} + +} // namespace ftxui From 9dfbdd168bff26064cba944915b486a7db763f4f Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 20:38:17 +0700 Subject: [PATCH 02/14] We can select in 2D --- examples/component/input.cpp | 28 ++++++++++++---------------- include/ftxui/dom/elements.hpp | 12 ++++++++++-- src/ftxui/dom/selected.cpp | 19 +++++++++---------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 67ef0bc..feafbba 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -21,11 +21,7 @@ int main() { std::string last_name; std::string password; std::string phoneNumber; - - int select_startx = -1; - int select_starty = -1; - int select_endx = -1; - int select_endy = -1; + Region selection; std::string textToCopy; auto screen = ScreenInteractive::TerminalOutput(); @@ -68,11 +64,11 @@ 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(select_startx) + ";" + std::to_string(select_starty)), - text("select_end " + std::to_string(select_endx) + ";" + std::to_string(select_endy)), + 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 | selected(select_startx, select_endx); + border | selected(selection); }); @@ -81,17 +77,17 @@ int main() { auto& mouse = event.mouse(); if (mouse.button == Mouse::Left) { if (mouse.motion == Mouse::Pressed) { - select_startx = mouse.x; - select_starty = mouse.y; - select_endx = mouse.x; - select_endy = mouse.y; + selection.startx = mouse.x; + selection.starty = mouse.y; + selection.endx = mouse.x; + selection.endy = mouse.y; } else if (mouse.motion == Mouse::Released) { - select_endx = mouse.x; - select_endy = mouse.y; + selection.endx = mouse.x; + selection.endy = mouse.y; } else if (mouse.motion == Mouse::Moved) { - select_endx = mouse.x; - select_endy = mouse.y; + selection.endx = mouse.x; + selection.endy = mouse.y; } screen.PostEvent(Event::Custom); diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 1b74f0c..71eb376 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -33,6 +33,14 @@ enum BorderStyle { EMPTY, }; +typedef struct { + + int startx; + int endx; + int starty; + int endy; +} Region; + // Pipe elements into decorator togethers. // For instance the next lines are equivalents: // -> text("ftxui") | bold | underlined @@ -96,8 +104,8 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); -Element selected(int &start, int &end, Element); -Decorator selected(int &start, int &end); +Element selected(Region &selection, Element); +Decorator selected(Region &selection); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp index f97b9bb..ebb9552 100644 --- a/src/ftxui/dom/selected.cpp +++ b/src/ftxui/dom/selected.cpp @@ -16,21 +16,20 @@ namespace { class Selected : public NodeDecorator { public: using NodeDecorator::NodeDecorator; - Selected(Element child, int &start, int &end) - : NodeDecorator(std::move(child)), startx_(start), endx_(end) {} + Selected(Element child, Region &selection) + : NodeDecorator(std::move(child)), selection_(selection) {} void Render(Screen& screen) override { Node::Render(screen); - for (int y = box_.y_min; y <= box_.y_max; ++y) { - for (int x = startx_; x <= endx_; ++x) { + for (int y = selection_.starty; y <= selection_.endy; ++y) { + for (int x = selection_.startx; x <= selection_.endx; ++x) { screen.PixelAt(x, y).inverted ^= true; } } } private: - int &startx_; - int &endx_; + Region &selection_; }; } // namespace @@ -38,12 +37,12 @@ private: /// colors. /// @ingroup dom -Element selected(int &start, int &end, Element child) { - return std::make_shared(std::move(child), start, end); +Element selected(Region &selection, Element child) { + return std::make_shared(std::move(child), selection); } -Decorator selected(int &start, int &end) { - return [&start, &end](Element child) { return selected(start, end, std::move(child)); }; +Decorator selected(Region &selection) { + return [&selection](Element child) { return selected(selection, std::move(child)); }; } } // namespace ftxui From e9d13e2c2cd9103e55578ef64bff7c755a068f91 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 21:28:35 +0700 Subject: [PATCH 03/14] We can select some text --- examples/component/input.cpp | 23 ++++++++++++++++------- include/ftxui/dom/elements.hpp | 4 ++-- src/ftxui/dom/selected.cpp | 15 +++++++++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index feafbba..aaa49cc 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -68,7 +68,7 @@ int main() { text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), text("textToCopy " + textToCopy) }) | - border | selected(selection); + border | selected(selection, textToCopy); }); @@ -76,17 +76,26 @@ int main() { if (event.is_mouse()) { auto& mouse = event.mouse(); if (mouse.button == Mouse::Left) { - if (mouse.motion == Mouse::Pressed) { + + if (mouse.motion == Mouse::Pressed) + { selection.startx = mouse.x; selection.starty = mouse.y; - selection.endx = mouse.x; + selection.endx = mouse.x-1; selection.endy = mouse.y; - } else if (mouse.motion == Mouse::Released) { - selection.endx = mouse.x; + + // screen.PixelAt(mouse.x, mouse.y).blink = true; + // screen.PixelAt(mouse.x, mouse.y).character = "K"; + textToCopy += screen.PixelAt(mouse.x, mouse.y).character; + } + else if (mouse.motion == Mouse::Released) + { + selection.endx = mouse.x-1; selection.endy = mouse.y; } - else if (mouse.motion == Mouse::Moved) { - selection.endx = mouse.x; + else if (mouse.motion == Mouse::Moved) + { + selection.endx = mouse.x-1; selection.endy = mouse.y; } diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 71eb376..517c21a 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -104,8 +104,8 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); -Element selected(Region &selection, Element); -Decorator selected(Region &selection); +Element selected(Region &selection, std::string &destination, Element); +Decorator selected(Region &selection, std::string &destination); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp index ebb9552..dec2253 100644 --- a/src/ftxui/dom/selected.cpp +++ b/src/ftxui/dom/selected.cpp @@ -16,20 +16,23 @@ namespace { class Selected : public NodeDecorator { public: using NodeDecorator::NodeDecorator; - Selected(Element child, Region &selection) - : NodeDecorator(std::move(child)), selection_(selection) {} + Selected(Element child, Region &selection, std::string &destination) + : NodeDecorator(std::move(child)), selection_(selection), destination_(destination) {} void Render(Screen& screen) override { Node::Render(screen); + destination_ = ""; for (int y = selection_.starty; y <= selection_.endy; ++y) { for (int x = selection_.startx; x <= selection_.endx; ++x) { screen.PixelAt(x, y).inverted ^= true; + destination_ += screen.PixelAt(x, y).character; } } } private: Region &selection_; + std::string &destination_; }; } // namespace @@ -37,12 +40,12 @@ private: /// colors. /// @ingroup dom -Element selected(Region &selection, Element child) { - return std::make_shared(std::move(child), selection); +Element selected(Region &selection, std::string &destination, Element child) { + return std::make_shared(std::move(child), selection, destination); } -Decorator selected(Region &selection) { - return [&selection](Element child) { return selected(selection, std::move(child)); }; +Decorator selected(Region &selection, std::string &destination) { + return [&selection, &destination](Element child) { return selected(selection, destination, std::move(child)); }; } } // namespace ftxui From 70b32fe523370e33dceb8fe7b257701c0581000e Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 21:36:12 +0700 Subject: [PATCH 04/14] Cleaner code --- examples/component/input.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index aaa49cc..1a0bcaa 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -71,6 +71,10 @@ int main() { border | selected(selection, textToCopy); }); + // TODO: Make the textToCopy a callback called every times the selected text change + // TODO: Implement the double click on word to select the word + // TODO: Implement the double click and drag to select word by word (optional) + // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? renderer |= CatchEvent([&](Event event) { if (event.is_mouse()) { @@ -83,10 +87,6 @@ int main() { selection.starty = mouse.y; selection.endx = mouse.x-1; selection.endy = mouse.y; - - // screen.PixelAt(mouse.x, mouse.y).blink = true; - // screen.PixelAt(mouse.x, mouse.y).character = "K"; - textToCopy += screen.PixelAt(mouse.x, mouse.y).character; } else if (mouse.motion == Mouse::Released) { From c1c6afc0baa5dd957b023f4a1784ea709405af8f Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 22:28:56 +0700 Subject: [PATCH 05/14] We can select in every directions --- examples/component/input.cpp | 7 ++++--- src/ftxui/dom/selected.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 1a0bcaa..61b3c0c 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -72,9 +72,10 @@ int main() { }); // TODO: Make the textToCopy a callback called every times the selected text change + // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? // TODO: Implement the double click on word to select the word // TODO: Implement the double click and drag to select word by word (optional) - // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? + // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things renderer |= CatchEvent([&](Event event) { if (event.is_mouse()) { @@ -85,7 +86,7 @@ int main() { { selection.startx = mouse.x; selection.starty = mouse.y; - selection.endx = mouse.x-1; + selection.endx = mouse.x; selection.endy = mouse.y; } else if (mouse.motion == Mouse::Released) @@ -95,7 +96,7 @@ int main() { } else if (mouse.motion == Mouse::Moved) { - selection.endx = mouse.x-1; + selection.endx = mouse.x; selection.endy = mouse.y; } diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp index dec2253..9adfe44 100644 --- a/src/ftxui/dom/selected.cpp +++ b/src/ftxui/dom/selected.cpp @@ -22,8 +22,8 @@ class Selected : public NodeDecorator { void Render(Screen& screen) override { Node::Render(screen); destination_ = ""; - for (int y = selection_.starty; y <= selection_.endy; ++y) { - for (int x = selection_.startx; x <= selection_.endx; ++x) { + for (int y = std::min(selection_.starty, selection_.endy); y <= std::max(selection_.starty, selection_.endy); ++y) { + for (int x = std::min(selection_.startx, selection_.endx); x <= std::max(selection_.startx, selection_.endx)-1; ++x) { screen.PixelAt(x, y).inverted ^= true; destination_ += screen.PixelAt(x, y).character; } From 719a2388f542617c0ec052661edc53eefbd65500 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 22:43:36 +0700 Subject: [PATCH 06/14] Corrected bug --- include/ftxui/dom/elements.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 517c21a..374bb5f 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -35,10 +35,10 @@ enum BorderStyle { typedef struct { - int startx; - int endx; - int starty; - int endy; + uint16_t startx = 0; + uint16_t endx = 0; + uint16_t starty = 0; + uint16_t endy = 0; } Region; // Pipe elements into decorator togethers. From 465822523d729c69117ac081a85e7f3d52c544fe Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 22:45:38 +0700 Subject: [PATCH 07/14] Corrected release bug --- examples/component/input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 61b3c0c..0e9aa1b 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -91,7 +91,7 @@ int main() { } else if (mouse.motion == Mouse::Released) { - selection.endx = mouse.x-1; + selection.endx = mouse.x; selection.endy = mouse.y; } else if (mouse.motion == Mouse::Moved) From 27424d5c205aeb5595dbfb5b67419bac5b96a496 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 22:45:50 +0700 Subject: [PATCH 08/14] Implemented a callback --- examples/component/input.cpp | 4 ++-- include/ftxui/dom/elements.hpp | 4 ++-- src/ftxui/dom/selected.cpp | 18 +++++++++++------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 0e9aa1b..cae9826 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -68,11 +68,11 @@ int main() { text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), text("textToCopy " + textToCopy) }) | - border | selected(selection, textToCopy); + border | selected(selection, textToCopy, [&textToCopy](std::string selected){textToCopy = selected;}); }); // TODO: Make the textToCopy a callback called every times the selected text change - // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? + // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? At a minimum move the function in the selected.cpp file and add doc to call it // TODO: Implement the double click on word to select the word // TODO: Implement the double click and drag to select word by word (optional) // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 374bb5f..458c839 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -104,8 +104,8 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); -Element selected(Region &selection, std::string &destination, Element); -Decorator selected(Region &selection, std::string &destination); +Element selected(Region &selection, std::string &destination, std::function onSelectionChange, Element); +Decorator selected(Region &selection, std::string &destination, std::function onSelectionChange); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp index 9adfe44..d1fc5a6 100644 --- a/src/ftxui/dom/selected.cpp +++ b/src/ftxui/dom/selected.cpp @@ -16,23 +16,27 @@ namespace { class Selected : public NodeDecorator { public: using NodeDecorator::NodeDecorator; - Selected(Element child, Region &selection, std::string &destination) - : NodeDecorator(std::move(child)), selection_(selection), destination_(destination) {} + Selected(Element child, Region &selection, std::string &destination, std::function onSelectionChange) + : NodeDecorator(std::move(child)), selection_(selection), destination_(destination), onSelectionChange_(onSelectionChange) {} void Render(Screen& screen) override { Node::Render(screen); destination_ = ""; + std::string textToCopy = ""; for (int y = std::min(selection_.starty, selection_.endy); y <= std::max(selection_.starty, selection_.endy); ++y) { for (int x = std::min(selection_.startx, selection_.endx); x <= std::max(selection_.startx, selection_.endx)-1; ++x) { screen.PixelAt(x, y).inverted ^= true; - destination_ += screen.PixelAt(x, y).character; + textToCopy += screen.PixelAt(x, y).character; } } + + onSelectionChange_(textToCopy); } private: Region &selection_; std::string &destination_; + std::function onSelectionChange_; }; } // namespace @@ -40,12 +44,12 @@ private: /// colors. /// @ingroup dom -Element selected(Region &selection, std::string &destination, Element child) { - return std::make_shared(std::move(child), selection, destination); +Element selected(Region &selection, std::string &destination, std::function onSelectionChange, Element child) { + return std::make_shared(std::move(child), selection, destination, onSelectionChange); } -Decorator selected(Region &selection, std::string &destination) { - return [&selection, &destination](Element child) { return selected(selection, destination, std::move(child)); }; +Decorator selected(Region &selection, std::string &destination, std::function onSelectionChange) { + return [&selection, &destination, onSelectionChange](Element child) { return selected(selection, destination, onSelectionChange, std::move(child)); }; } } // namespace ftxui From 10a4a045297022499d3659b29845bb3df022056d Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 22:48:13 +0700 Subject: [PATCH 09/14] Cleaner code --- examples/component/input.cpp | 3 +-- include/ftxui/dom/elements.hpp | 4 ++-- src/ftxui/dom/selected.cpp | 20 +++++++++----------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index cae9826..b83a73a 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -68,10 +68,9 @@ int main() { text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), text("textToCopy " + textToCopy) }) | - border | selected(selection, textToCopy, [&textToCopy](std::string selected){textToCopy = selected;}); + border | selected(selection, [&textToCopy](std::string selected){textToCopy = selected;}); }); - // TODO: Make the textToCopy a callback called every times the selected text change // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? At a minimum move the function in the selected.cpp file and add doc to call it // TODO: Implement the double click on word to select the word // TODO: Implement the double click and drag to select word by word (optional) diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 458c839..3f4db35 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -104,8 +104,8 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); -Element selected(Region &selection, std::string &destination, std::function onSelectionChange, Element); -Decorator selected(Region &selection, std::string &destination, std::function onSelectionChange); +Element selected(Region &selection, std::function onSelectionChange, Element); +Decorator selected(Region &selection, std::function onSelectionChange); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp index d1fc5a6..701b688 100644 --- a/src/ftxui/dom/selected.cpp +++ b/src/ftxui/dom/selected.cpp @@ -16,26 +16,24 @@ namespace { class Selected : public NodeDecorator { public: using NodeDecorator::NodeDecorator; - Selected(Element child, Region &selection, std::string &destination, std::function onSelectionChange) - : NodeDecorator(std::move(child)), selection_(selection), destination_(destination), onSelectionChange_(onSelectionChange) {} + Selected(Element child, Region &selection, std::function onSelectionChange) + : NodeDecorator(std::move(child)), selection_(selection), onSelectionChange_(onSelectionChange) {} void Render(Screen& screen) override { Node::Render(screen); - destination_ = ""; - std::string textToCopy = ""; + std::string selectedText = ""; for (int y = std::min(selection_.starty, selection_.endy); y <= std::max(selection_.starty, selection_.endy); ++y) { for (int x = std::min(selection_.startx, selection_.endx); x <= std::max(selection_.startx, selection_.endx)-1; ++x) { screen.PixelAt(x, y).inverted ^= true; - textToCopy += screen.PixelAt(x, y).character; + selectedText += screen.PixelAt(x, y).character; } } - onSelectionChange_(textToCopy); + onSelectionChange_(selectedText); } private: Region &selection_; - std::string &destination_; std::function onSelectionChange_; }; } // namespace @@ -44,12 +42,12 @@ private: /// colors. /// @ingroup dom -Element selected(Region &selection, std::string &destination, std::function onSelectionChange, Element child) { - return std::make_shared(std::move(child), selection, destination, onSelectionChange); +Element selected(Region &selection, std::function onSelectionChange, Element child) { + return std::make_shared(std::move(child), selection, onSelectionChange); } -Decorator selected(Region &selection, std::string &destination, std::function onSelectionChange) { - return [&selection, &destination, onSelectionChange](Element child) { return selected(selection, destination, onSelectionChange, std::move(child)); }; +Decorator selected(Region &selection, std::function onSelectionChange) { + return [&selection, onSelectionChange](Element child) { return selected(selection, onSelectionChange, std::move(child)); }; } } // namespace ftxui From 2de925f2c6141ed7213242e7cf5fce9851a0cd4e Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 23:03:37 +0700 Subject: [PATCH 10/14] Better integratiion --- CMakeLists.txt | 2 +- examples/component/input.cpp | 42 ++---------------- include/ftxui/dom/elements.hpp | 6 ++- src/ftxui/dom/selectable.cpp | 80 ++++++++++++++++++++++++++++++++++ src/ftxui/dom/selected.cpp | 53 ---------------------- 5 files changed, 89 insertions(+), 94 deletions(-) create mode 100644 src/ftxui/dom/selectable.cpp delete mode 100644 src/ftxui/dom/selected.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 350278e..2444657 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ add_library(dom src/ftxui/dom/gridbox.cpp src/ftxui/dom/hbox.cpp src/ftxui/dom/inverted.cpp - src/ftxui/dom/selected.cpp + src/ftxui/dom/selectable.cpp src/ftxui/dom/linear_gradient.cpp src/ftxui/dom/node.cpp src/ftxui/dom/node_decorator.cpp diff --git a/examples/component/input.cpp b/examples/component/input.cpp index b83a73a..23962ce 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -68,52 +68,18 @@ int main() { text("select_end " + std::to_string(selection.endx) + ";" + std::to_string(selection.endy)), text("textToCopy " + textToCopy) }) | - border | selected(selection, [&textToCopy](std::string selected){textToCopy = selected;}); + border | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}); }); - // TODO: Is there a way for me to embedd the catchEvent in the selected decorator? At a minimum move the function in the selected.cpp file and add doc to call it // TODO: Implement the double click on word to select the word // TODO: Implement the double click and drag to select word by word (optional) + // TODO: Implement the tripple click to select an entire line + // TODO: Call onSelectionChange_ only when the selection indeed did change, not at every render? // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things renderer |= CatchEvent([&](Event event) { - if (event.is_mouse()) { - auto& mouse = event.mouse(); - if (mouse.button == Mouse::Left) { - if (mouse.motion == Mouse::Pressed) - { - selection.startx = mouse.x; - selection.starty = mouse.y; - selection.endx = mouse.x; - selection.endy = mouse.y; - } - else if (mouse.motion == Mouse::Released) - { - selection.endx = mouse.x; - selection.endy = mouse.y; - } - else if (mouse.motion == Mouse::Moved) - { - selection.endx = mouse.x; - selection.endy = mouse.y; - } - - screen.PostEvent(Event::Custom); - return true; - } - } - - // if (event == Event::SpecialKey("Ctrl+Shift+C")) { - // textToCopy = "Kikoo!"; - // //clip::set_text(text_to_copy); // Set the clipboard content - - // screen.PostEvent(Event::Custom); - - // return true; - // } - - return false; + return selectableCatchEvent(event); }); screen.Loop(renderer); diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 3f4db35..4579c8a 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -16,6 +16,7 @@ #include "ftxui/screen/color.hpp" #include "ftxui/screen/terminal.hpp" #include "ftxui/util/ref.hpp" +#include "ftxui/component/event.hpp" namespace ftxui { class Node; @@ -104,8 +105,9 @@ Element canvas(std::function); Element bold(Element); Element dim(Element); Element inverted(Element); -Element selected(Region &selection, std::function onSelectionChange, Element); -Decorator selected(Region &selection, std::function onSelectionChange); +Element selectable(std::function onSelectionChange, Element); +Decorator selectable(std::function onSelectionChange); +bool selectableCatchEvent(Event event); Element underlined(Element); Element underlinedDouble(Element); Element blink(Element); diff --git a/src/ftxui/dom/selectable.cpp b/src/ftxui/dom/selectable.cpp new file mode 100644 index 0000000..f88a027 --- /dev/null +++ b/src/ftxui/dom/selectable.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 make_shared +#include // for move + +#include "ftxui/dom/elements.hpp" // for Element, inverted +#include "ftxui/dom/node.hpp" // for Node +#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator +#include "ftxui/screen/box.hpp" // for Box +#include "ftxui/screen/screen.hpp" // for Pixel, Screen + +namespace ftxui { + +Region newSelection; + +namespace { +class Selectable : public NodeDecorator { + public: + using NodeDecorator::NodeDecorator; + Selectable(Element child, std::function onSelectionChange) + : NodeDecorator(std::move(child)), onSelectionChange_(onSelectionChange) {} + + void Render(Screen& screen) override { + Node::Render(screen); + std::string selectedText = ""; + for (int y = std::min(newSelection.starty, newSelection.endy); y <= std::max(newSelection.starty, newSelection.endy); ++y) { + for (int x = std::min(newSelection.startx, newSelection.endx); x <= std::max(newSelection.startx, newSelection.endx)-1; ++x) { + screen.PixelAt(x, y).inverted ^= true; + selectedText += screen.PixelAt(x, y).character; + } + } + + onSelectionChange_(selectedText); + } + +private: + std::function onSelectionChange_; +}; +} // namespace + +/// @brief Add a filter that will invert the foreground and the background +/// colors. +/// @ingroup dom + +Element selectable(std::function onSelectionChange, Element child) { + return std::make_shared(std::move(child), onSelectionChange); +} + +Decorator selectable(std::function onSelectionChange) { + return [onSelectionChange](Element child) { return selectable(onSelectionChange, std::move(child)); }; +} + +bool selectableCatchEvent(Event event) { + + if (event.is_mouse()) { + auto& mouse = event.mouse(); + if (mouse.button == Mouse::Left) { + + if (mouse.motion == Mouse::Pressed) { + newSelection.startx = mouse.x; + newSelection.starty = mouse.y; + newSelection.endx = mouse.x; + newSelection.endy = mouse.y; + } else if (mouse.motion == Mouse::Released) { + newSelection.endx = mouse.x; + newSelection.endy = mouse.y; + } else if (mouse.motion == Mouse::Moved) { + newSelection.endx = mouse.x; + newSelection.endy = mouse.y; + } + + return true; + } + } + + return false; +} + +} // namespace ftxui diff --git a/src/ftxui/dom/selected.cpp b/src/ftxui/dom/selected.cpp deleted file mode 100644 index 701b688..0000000 --- a/src/ftxui/dom/selected.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// 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 make_shared -#include // for move - -#include "ftxui/dom/elements.hpp" // for Element, inverted -#include "ftxui/dom/node.hpp" // for Node -#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator -#include "ftxui/screen/box.hpp" // for Box -#include "ftxui/screen/screen.hpp" // for Pixel, Screen - -namespace ftxui { - -namespace { -class Selected : public NodeDecorator { - public: - using NodeDecorator::NodeDecorator; - Selected(Element child, Region &selection, std::function onSelectionChange) - : NodeDecorator(std::move(child)), selection_(selection), onSelectionChange_(onSelectionChange) {} - - void Render(Screen& screen) override { - Node::Render(screen); - std::string selectedText = ""; - for (int y = std::min(selection_.starty, selection_.endy); y <= std::max(selection_.starty, selection_.endy); ++y) { - for (int x = std::min(selection_.startx, selection_.endx); x <= std::max(selection_.startx, selection_.endx)-1; ++x) { - screen.PixelAt(x, y).inverted ^= true; - selectedText += screen.PixelAt(x, y).character; - } - } - - onSelectionChange_(selectedText); - } - -private: - Region &selection_; - std::function onSelectionChange_; -}; -} // namespace - -/// @brief Add a filter that will invert the foreground and the background -/// colors. -/// @ingroup dom - -Element selected(Region &selection, std::function onSelectionChange, Element child) { - return std::make_shared(std::move(child), selection, onSelectionChange); -} - -Decorator selected(Region &selection, std::function onSelectionChange) { - return [&selection, onSelectionChange](Element child) { return selected(selection, onSelectionChange, std::move(child)); }; -} - -} // namespace ftxui From 53f179c776ab942ca6373b5c58b3c29e222705d6 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Fri, 2 Aug 2024 23:08:24 +0700 Subject: [PATCH 11/14] Prevent spamming --- examples/component/input.cpp | 1 - include/ftxui/dom/elements.hpp | 1 + src/ftxui/dom/selectable.cpp | 30 ++++++++++++++++++------------ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index 23962ce..a4c31ec 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -74,7 +74,6 @@ int main() { // TODO: Implement the double click on word to select the word // TODO: Implement the double click and drag to select word by word (optional) // TODO: Implement the tripple click to select an entire line - // TODO: Call onSelectionChange_ only when the selection indeed did change, not at every render? // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things renderer |= CatchEvent([&](Event event) { diff --git a/include/ftxui/dom/elements.hpp b/include/ftxui/dom/elements.hpp index 4579c8a..f2a65b4 100644 --- a/include/ftxui/dom/elements.hpp +++ b/include/ftxui/dom/elements.hpp @@ -40,6 +40,7 @@ typedef struct { uint16_t endx = 0; uint16_t starty = 0; uint16_t endy = 0; + bool changed = false; } Region; // Pipe elements into decorator togethers. diff --git a/src/ftxui/dom/selectable.cpp b/src/ftxui/dom/selectable.cpp index f88a027..d1482ff 100644 --- a/src/ftxui/dom/selectable.cpp +++ b/src/ftxui/dom/selectable.cpp @@ -12,7 +12,7 @@ namespace ftxui { -Region newSelection; +Region selectedRegion; namespace { class Selectable : public NodeDecorator { @@ -24,14 +24,17 @@ class Selectable : public NodeDecorator { void Render(Screen& screen) override { Node::Render(screen); std::string selectedText = ""; - for (int y = std::min(newSelection.starty, newSelection.endy); y <= std::max(newSelection.starty, newSelection.endy); ++y) { - for (int x = std::min(newSelection.startx, newSelection.endx); x <= std::max(newSelection.startx, newSelection.endx)-1; ++x) { + 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) { screen.PixelAt(x, y).inverted ^= true; selectedText += screen.PixelAt(x, y).character; } } - onSelectionChange_(selectedText); + if(selectedRegion.changed == true) { + selectedRegion.changed = false;; + onSelectionChange_(selectedText); + } } private: @@ -58,16 +61,19 @@ bool selectableCatchEvent(Event event) { if (mouse.button == Mouse::Left) { if (mouse.motion == Mouse::Pressed) { - newSelection.startx = mouse.x; - newSelection.starty = mouse.y; - newSelection.endx = mouse.x; - newSelection.endy = mouse.y; + 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) { - newSelection.endx = mouse.x; - newSelection.endy = mouse.y; + selectedRegion.endx = mouse.x; + selectedRegion.endy = mouse.y; + selectedRegion.changed = true; } else if (mouse.motion == Mouse::Moved) { - newSelection.endx = mouse.x; - newSelection.endy = mouse.y; + selectedRegion.endx = mouse.x; + selectedRegion.endy = mouse.y; + selectedRegion.changed = true; } return true; From e3dd95d63c21674dc229f29036039ac46328ae1e Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Wed, 7 Aug 2024 13:24:57 +0700 Subject: [PATCH 12/14] Moved the example to its own file --- examples/component/CMakeLists.txt | 1 + examples/component/input.cpp | 21 +----- examples/component/selectable_input.cpp | 85 +++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 examples/component/selectable_input.cpp diff --git a/examples/component/CMakeLists.txt b/examples/component/CMakeLists.txt index 5d80fd8..fadf647 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(slider) example(slider_direction) example(slider_rgb) diff --git a/examples/component/input.cpp b/examples/component/input.cpp index a4c31ec..9768359 100644 --- a/examples/component/input.cpp +++ b/examples/component/input.cpp @@ -12,7 +12,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; @@ -21,10 +20,6 @@ int main() { 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"); @@ -64,22 +59,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 " + textToCopy) }) | - border | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}); - }); - - // TODO: Implement the double click on word to select the word - // TODO: Implement the double click and drag to select word by word (optional) - // TODO: Implement the tripple click to select an entire line - // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things - - renderer |= CatchEvent([&](Event event) { - - return selectableCatchEvent(event); + border; }); + auto screen = ScreenInteractive::TerminalOutput(); screen.Loop(renderer); } diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp new file mode 100644 index 0000000..a4c31ec --- /dev/null +++ b/examples/component/selectable_input.cpp @@ -0,0 +1,85 @@ +// 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 : "), 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;}); + }); + + // TODO: Implement the double click on word to select the word + // TODO: Implement the double click and drag to select word by word (optional) + // TODO: Implement the tripple click to select an entire line + // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things + + renderer |= CatchEvent([&](Event event) { + + return selectableCatchEvent(event); + }); + + screen.Loop(renderer); +} From eb21c23f6068d98fe44b1fb3b2cb8d4bd9dc19e7 Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Wed, 7 Aug 2024 13:28:13 +0700 Subject: [PATCH 13/14] Don't hold the event --- src/ftxui/dom/selectable.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ftxui/dom/selectable.cpp b/src/ftxui/dom/selectable.cpp index d1482ff..50a18e7 100644 --- a/src/ftxui/dom/selectable.cpp +++ b/src/ftxui/dom/selectable.cpp @@ -75,8 +75,6 @@ bool selectableCatchEvent(Event event) { selectedRegion.endy = mouse.y; selectedRegion.changed = true; } - - return true; } } From 7cdf9a4890904e7071577e59adcd36e896f6427e Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Wed, 7 Aug 2024 13:44:22 +0700 Subject: [PATCH 14/14] Removed todolist --- examples/component/selectable_input.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index a4c31ec..5be4512 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -71,11 +71,6 @@ int main() { border | selectable([&textToCopy](std::string txtSelected){textToCopy = txtSelected;}); }); - // TODO: Implement the double click on word to select the word - // TODO: Implement the double click and drag to select word by word (optional) - // TODO: Implement the tripple click to select an entire line - // TODO: Add a "selectable" flag in the pixel class and take it into account when selecting things - renderer |= CatchEvent([&](Event event) { return selectableCatchEvent(event);