mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-28 16:29:34 +08:00
Feature: Selection
Add support for selection content in the dom.
This commit is contained in:
168
src/ftxui/dom/selection.cpp
Normal file
168
src/ftxui/dom/selection.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
// 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 <algorithm> // 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
|
Reference in New Issue
Block a user