// 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 Box #include #include // for move #include #include "ftxui/dom/node.hpp" #include "ftxui/dom/selection.hpp" // for Selection #include "ftxui/screen/screen.hpp" // for Screen namespace ftxui { Node::Node() = default; Node::Node(Elements children) : children_(std::move(children)) {} Node::~Node() = default; /// @brief Compute how much space an elements needs. /// @ingroup dom void Node::ComputeRequirement() { if (children_.empty()) { return; } for (auto& child : children_) { child->ComputeRequirement(); } // By default, the requirement is the one of the first child. requirement_ = children_[0]->requirement(); // Propagate the focused requirement. for (size_t i = 1; i < children_.size(); ++i) { if (!requirement_.focused.enabled && children_[i]->requirement().focused.enabled) { requirement_.focused = children_[i]->requirement().focused; } } } /// @brief Assign a position and a dimension to an element for drawing. /// @ingroup dom void Node::SetBox(Box box) { box_ = box; } /// @brief Compute the selection of an element. /// @ingroup dom void Node::Select(Selection& selection) { // If this Node box_ doesn't intersect with the selection, then no selection. if (Box::Intersection(selection.GetBox(), box_).IsEmpty()) { return; } // By default we defer the selection to the children. for (auto& child : children_) { child->Select(selection); } } /// @brief Display an element on a ftxui::Screen. /// @ingroup dom void Node::Render(Screen& screen) { for (auto& child : children_) { child->Render(screen); } } void Node::Check(Status* status) { for (auto& child : children_) { child->Check(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) { Selection selection; Render(screen, element.get(), selection); } /// @brief Display an element on a ftxui::Screen. /// @ingroup dom void Render(Screen& screen, Node* node) { Selection selection; Render(screen, node, selection); } void Render(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 if (!selection.IsEmpty()) { node->Select(selection); } if (node->requirement().focused.enabled #if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK) // Setting the cursor to the right position allow folks using CJK (China, // Japanese, Korean, ...) characters to see their [input method editor] // displayed at the right location. See [issue]. // // [input method editor]: // https://en.wikipedia.org/wiki/Input_method // // [issue]: // https://github.com/ArthurSonzogni/FTXUI/issues/2#issuecomment-505282355 // // Unfortunately, Microsoft terminal do not handle properly hiding the // cursor. Instead the character under the cursor is hidden, which is a // big problem. As a result, we can't enable setting cursor to the right // location. It will be displayed at the bottom right corner. // See: // https://github.com/microsoft/terminal/issues/1203 // https://github.com/microsoft/terminal/issues/3093 && !node->requirement().focused.cursor_shape == Screen::Cursor::Shape::Hidden #endif ) { screen.SetCursor(Screen::Cursor{ node->requirement().focused.node->box_.x_max, node->requirement().focused.node->box_.y_max, node->requirement().focused.cursor_shape, }); } else { screen.SetCursor(Screen::Cursor{ screen.dimx() - 1, screen.dimy() - 1, Screen::Cursor::Shape::Hidden, }); } // Step 4: Draw the element. screen.stencil = box; node->Render(screen); // Step 5: Apply shaders 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