// Copyright 2024 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 "ftxui/dom/selection.hpp" // for Selection #include // for max, min #include "ftxui/dom/elements.hpp" // for Element, inverted #include "ftxui/dom/node_decorator.hpp" // for NodeDecorator namespace ftxui { namespace { class Unselectable : public NodeDecorator { public: using NodeDecorator::NodeDecorator; void Select(Selection& selection) override { // Overwrite the select method to do nothing. } }; } // namespace /// @brief Create an empty selection. Selection::Selection() : empty_(true) {} /// @brief Create a selection. /// @param start_x The x coordinate of the start of the selection. /// @param start_y The y coordinate of the start of the selection. /// @param end_x The x coordinate of the end of the selection. /// @param end_y The y coordinate of the end of the selection. Selection::Selection(int start_x, int start_y, int end_x, int end_y) : start_x_(start_x), start_y_(start_y), end_x_(end_x), end_y_(end_y), box_{ std::min(start_x, end_x), std::max(start_x, end_x), std::min(start_y, end_y), std::max(start_y, end_y), }, empty_(false) {} Selection::Selection(int start_x, int start_y, int end_x, int end_y, Selection* parent) : start_x_(start_x), start_y_(start_y), end_x_(end_x), end_y_(end_y), box_{ std::min(start_x, end_x), std::max(start_x, end_x), std::min(start_y, end_y), std::max(start_y, end_y), }, parent_(parent), empty_(false) {} /// @brief Get the box of the selection. /// @return The box of the selection. const Box& Selection::GetBox() const { return box_; } /// @brief Saturate the selection to be inside the box. /// This is called by `hbox` to propagate the selection to its children. /// @param box The box to saturate the selection in. /// @return The saturated selection. Selection Selection::SaturateHorizontal(Box box) { int start_x = start_x_; int start_y = start_y_; int end_x = end_x_; int end_y = end_y_; const bool start_outside = !box.Contain(start_x, start_y); const bool end_outside = !box.Contain(end_x, end_y); const bool properly_ordered = start_y < end_y || (start_y == end_y && start_x <= end_x); if (properly_ordered) { if (start_outside) { start_x = box.x_min; start_y = box.y_min; } if (end_outside) { end_x = box.x_max; end_y = box.y_max; } } else { if (start_outside) { start_x = box.x_max; start_y = box.y_max; } if (end_outside) { end_x = box.x_min; end_y = box.y_min; } } return Selection(start_x, start_y, end_x, end_y, parent_); } /// @brief Saturate the selection to be inside the box. /// This is called by `vbox` to propagate the selection to its children. /// @param box The box to saturate the selection in. /// @return The saturated selection. Selection Selection::SaturateVertical(Box box) { int start_x = start_x_; int start_y = start_y_; int end_x = end_x_; int end_y = end_y_; const bool start_outside = !box.Contain(start_x, start_y); const bool end_outside = !box.Contain(end_x, end_y); const bool properly_ordered = start_y < end_y || (start_y == end_y && start_x <= end_x); if (properly_ordered) { if (start_outside) { start_x = box.x_min; start_y = box.y_min; } if (end_outside) { end_x = box.x_max; end_y = box.y_max; } } else { if (start_outside) { start_x = box.x_max; start_y = box.y_max; } if (end_outside) { end_x = box.x_min; end_y = box.y_min; } } return Selection(start_x, start_y, end_x, end_y, parent_); } void Selection::AddPart(const std::string& part, int y, int left, int right) { if (parent_ != this) { return parent_->AddPart(part, y, left, right); } [&] { if (parts_.str().empty()) { parts_ << part; return; } if (y_ != y) { parts_ << '\n' << part; return; } if (x_ == left + 1) { parts_ << part; return; } parts_ << part; }(); y_ = y; x_ = right; } } // namespace ftxui