From 307d8b51bf3c9d14999ec0b38ef146a92442760f Mon Sep 17 00:00:00 2001 From: Clement Roblot Date: Mon, 2 Dec 2024 16:47:13 +0700 Subject: [PATCH] We can retrieve the content of the selection --- examples/component/selectable_input.cpp | 15 ++++--- .../ftxui/component/screen_interactive.hpp | 3 +- include/ftxui/dom/node.hpp | 3 ++ src/ftxui/component/screen_interactive.cpp | 5 ++- src/ftxui/dom/node.cpp | 42 +++++++++++++++++++ src/ftxui/dom/text.cpp | 24 +++++++++++ 6 files changed, 83 insertions(+), 9 deletions(-) diff --git a/examples/component/selectable_input.cpp b/examples/component/selectable_input.cpp index 779e3283..1055c65d 100644 --- a/examples/component/selectable_input.cpp +++ b/examples/component/selectable_input.cpp @@ -27,18 +27,16 @@ Element LoremIpsum() { int main() { auto screen = ScreenInteractive::TerminalOutput(); - int counter = 0; - - screen.onSelectionModified([&]{ - counter++; - }); + int selectionChangeCounter = 0; + std::string selection = ""; auto quit = Button("Quit", screen.ExitLoopClosure()); // The components: auto renderer = Renderer(quit, [&] { return vbox({ - text("Select: " + std::to_string(counter)), + text("Select changed: " + std::to_string(selectionChangeCounter) + " times"), + text("Currently selected: " + selection), window(text("Horizontal split"), hbox({ LoremIpsum(), separator(), @@ -75,5 +73,10 @@ int main() { }); }); + screen.onSelectionModified([&] { + selectionChangeCounter++; + selection = screen.GetSelectedContent(renderer); + }); + screen.Loop(renderer); } diff --git a/include/ftxui/component/screen_interactive.hpp b/include/ftxui/component/screen_interactive.hpp index 1c0f8685..3e09fe0d 100644 --- a/include/ftxui/component/screen_interactive.hpp +++ b/include/ftxui/component/screen_interactive.hpp @@ -69,8 +69,7 @@ class ScreenInteractive : public Screen { void ForceHandleCtrlZ(bool force); // Selection API. - // TODO: Implement somethings here. - std::string GetSelectedContent(void); + std::string GetSelectedContent(Component component); void onSelectionModified(std::function callback); private: diff --git a/include/ftxui/dom/node.hpp b/include/ftxui/dom/node.hpp index ea4903d8..a59e942c 100644 --- a/include/ftxui/dom/node.hpp +++ b/include/ftxui/dom/node.hpp @@ -48,6 +48,8 @@ class Node { // Step 4: Draw this element. virtual void Render(Screen& screen); + virtual std::string GetSelectedContent(Selection& selection); + // Layout may not resolve within a single iteration for some elements. This // allows them to request additionnal iterations. This signal must be // forwarded to children at least once. @@ -66,6 +68,7 @@ class Node { void Render(Screen& screen, const Element& element); void Render(Screen& screen, Node* node); void Render(Screen& screen, Node* node, Selection& selection); +std::string GetNodeSelectedContent(Screen& screen, Node* node, Selection& selection); } // namespace ftxui diff --git a/src/ftxui/component/screen_interactive.cpp b/src/ftxui/component/screen_interactive.cpp index dab73ecb..caac62cb 100644 --- a/src/ftxui/component/screen_interactive.cpp +++ b/src/ftxui/component/screen_interactive.cpp @@ -577,9 +577,12 @@ void ScreenInteractive::ForceHandleCtrlZ(bool force) { } /// @brief Returns the content of the current selection -std::string ScreenInteractive::GetSelectedContent(void) +std::string ScreenInteractive::GetSelectedContent(Component component) { + Selection selection(selection_start_x_, selection_start_y_, // + selection_end_x_, selection_end_y_); + return GetNodeSelectedContent(*this, component->Render().get(), selection); } /// @brief Sets a callback on modifications of the selection diff --git a/src/ftxui/dom/node.cpp b/src/ftxui/dom/node.cpp index c52da90b..0bf71d84 100644 --- a/src/ftxui/dom/node.cpp +++ b/src/ftxui/dom/node.cpp @@ -57,6 +57,17 @@ void Node::Check(Status* status) { status->need_iteration |= (status->iteration == 0); } +std::string Node::GetSelectedContent(Selection& selection) { + + std::string content; + + for (auto& child : children_) { + content += child->GetSelectedContent(selection); + } + + return content; +} + /// @brief Display an element on a ftxui::Screen. /// @ingroup dom void Render(Screen& screen, const Element& element) { @@ -105,4 +116,35 @@ void Render(Screen& screen, Node* node, Selection& selection) { screen.ApplyShader(); } +std::string GetNodeSelectedContent(Screen& screen, Node* node, Selection& selection) { + + Box box; + box.x_min = 0; + box.y_min = 0; + box.x_max = screen.dimx() - 1; + box.y_max = screen.dimy() - 1; + + Node::Status status; + node->Check(&status); + const int max_iterations = 20; + while (status.need_iteration && status.iteration < max_iterations) { + // Step 1: Find what dimension this elements wants to be. + node->ComputeRequirement(); + + // Step 2: Assign a dimension to the element. + node->SetBox(box); + + // Check if the element needs another iteration of the layout algorithm. + status.need_iteration = false; + status.iteration++; + node->Check(&status); + } + + // Step 3: Selection + node->Select(selection); + + // Step 4: get the selected content. + return node->GetSelectedContent(selection); +} + } // namespace ftxui diff --git a/src/ftxui/dom/text.cpp b/src/ftxui/dom/text.cpp index 68b5cfe6..f2b70903 100644 --- a/src/ftxui/dom/text.cpp +++ b/src/ftxui/dom/text.cpp @@ -44,6 +44,7 @@ class Text : public Node { void Render(Screen& screen) override { int x = box_.x_min; const int y = box_.y_min; + if (y > box_.y_max) { return; } @@ -67,6 +68,29 @@ class Text : public Node { } } + std::string GetSelectedContent(Selection& selection) { + int x = box_.x_min; + std::string selected_text = ""; + + if (has_selection == false) { + return ""; + } + + for (const auto& cell : Utf8ToGlyphs(text_)) { + if (x > box_.x_max) { + break; + } + + if ((x >= selection_start_) && (x <= selection_end_)) { + selected_text += cell; + } + + ++x; + } + + return selected_text; + } + private: std::string text_; bool has_selection = false;