mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 18:59:59 +08:00
Start the tree-aware selection.
This commit is contained in:
parent
143d152e3d
commit
d38f3d229a
@ -10,62 +10,63 @@
|
|||||||
#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border
|
#include "ftxui/dom/elements.hpp" // for text, hbox, separator, Element, operator|, vbox, border
|
||||||
#include "ftxui/util/ref.hpp" // for Ref
|
#include "ftxui/util/ref.hpp" // for Ref
|
||||||
|
|
||||||
|
using namespace ftxui;
|
||||||
|
|
||||||
|
Element LoremIpsum() {
|
||||||
|
return vbox({
|
||||||
|
text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
|
||||||
|
"eiusmod tempor incididunt ut labore et dolore magna aliqua."),
|
||||||
|
text("Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris "
|
||||||
|
"nisi ut aliquip ex ea commodo consequat."),
|
||||||
|
text("Duis aute irure dolor in reprehenderit in voluptate velit esse "
|
||||||
|
"cillum dolore eu fugiat nulla pariatur."),
|
||||||
|
text("Excepteur sint occaecat cupidatat non proident, sunt in culpa qui "
|
||||||
|
"officia deserunt mollit anim id est laborum."),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
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();
|
auto screen = ScreenInteractive::TerminalOutput();
|
||||||
|
|
||||||
// The basic input components:
|
auto quit = Button("Quit", screen.ExitLoopClosure());
|
||||||
Component input_first_name = Input(&first_name, "first name");
|
|
||||||
Component input_last_name = Input(&last_name, "last name");
|
|
||||||
|
|
||||||
// The password input component:
|
// The components:
|
||||||
InputOption password_option;
|
auto renderer = Renderer(quit, [&] {
|
||||||
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([&](const Event& event) {
|
|
||||||
return event.is_character() && !std::isdigit(event.character()[0]);
|
|
||||||
});
|
|
||||||
input_phone_number |= CatchEvent([&](const 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({
|
return vbox({
|
||||||
hbox(text(" First name : "), input_first_name->Render()),
|
window(text("Horizontal split"), hbox({
|
||||||
hbox(text(" Last name : ") | selectable(),
|
LoremIpsum(),
|
||||||
input_last_name->Render()),
|
separator(),
|
||||||
hbox(text(" Password : "), input_password->Render()),
|
LoremIpsum(),
|
||||||
hbox(text(" Phone num : "), input_phone_number->Render()) |
|
separator(),
|
||||||
selectable(),
|
LoremIpsum(),
|
||||||
separator(),
|
})),
|
||||||
text("Hello " + first_name + " " + last_name),
|
window(text("Vertical split"), vbox({
|
||||||
text("Your password is " + password),
|
LoremIpsum(),
|
||||||
text("Your phone number is " + phoneNumber),
|
separator(),
|
||||||
text("Selected test is " + screen.GetSelection()),
|
LoremIpsum(),
|
||||||
}) |
|
separator(),
|
||||||
border;
|
LoremIpsum(),
|
||||||
|
})),
|
||||||
|
window(text("Vertical split"),
|
||||||
|
vbox({
|
||||||
|
window(text("horizontal split"), hbox({
|
||||||
|
LoremIpsum(),
|
||||||
|
separator(),
|
||||||
|
LoremIpsum(),
|
||||||
|
separator(),
|
||||||
|
LoremIpsum(),
|
||||||
|
})),
|
||||||
|
separator(),
|
||||||
|
window(text("horizontal split"), hbox({
|
||||||
|
LoremIpsum(),
|
||||||
|
separator(),
|
||||||
|
LoremIpsum(),
|
||||||
|
separator(),
|
||||||
|
LoremIpsum(),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
quit->Render(),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
screen.Loop(renderer);
|
screen.Loop(renderer);
|
||||||
|
@ -68,7 +68,9 @@ class ScreenInteractive : public Screen {
|
|||||||
void ForceHandleCtrlC(bool force);
|
void ForceHandleCtrlC(bool force);
|
||||||
void ForceHandleCtrlZ(bool force);
|
void ForceHandleCtrlZ(bool force);
|
||||||
|
|
||||||
std::string GetSelection();
|
// Selection API.
|
||||||
|
//void OnSelectionChange(std::function<void(std::
|
||||||
|
//void ClearSelection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ExitNow();
|
void ExitNow();
|
||||||
@ -133,6 +135,11 @@ class ScreenInteractive : public Screen {
|
|||||||
// The style of the cursor to restore on exit.
|
// The style of the cursor to restore on exit.
|
||||||
int cursor_reset_shape_ = 1;
|
int cursor_reset_shape_ = 1;
|
||||||
|
|
||||||
|
// Selection API:
|
||||||
|
bool selection_enabled_ = false;
|
||||||
|
CapturedMouse selection_pending_;
|
||||||
|
Box selection_box_;
|
||||||
|
|
||||||
friend class Loop;
|
friend class Loop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#ifndef FTXUI_DOM_NODE_HPP
|
#ifndef FTXUI_DOM_NODE_HPP
|
||||||
#define FTXUI_DOM_NODE_HPP
|
#define FTXUI_DOM_NODE_HPP
|
||||||
|
|
||||||
|
#include <list> // for list
|
||||||
#include <memory> // for shared_ptr
|
#include <memory> // for shared_ptr
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
@ -40,7 +41,11 @@ class Node {
|
|||||||
// Propagated from Parents to Children.
|
// Propagated from Parents to Children.
|
||||||
virtual void SetBox(Box box);
|
virtual void SetBox(Box box);
|
||||||
|
|
||||||
// Step 3: Draw this element.
|
// Step 3: (optional) Selection
|
||||||
|
// Propagated from Parents to Children.
|
||||||
|
virtual void Selection(Box selection, std::vector<Box>* selected);
|
||||||
|
|
||||||
|
// Step 4: Draw this element.
|
||||||
virtual void Render(Screen& screen);
|
virtual void Render(Screen& screen);
|
||||||
|
|
||||||
// Layout may not resolve within a single iteration for some elements. This
|
// Layout may not resolve within a single iteration for some elements. This
|
||||||
@ -52,11 +57,6 @@ class Node {
|
|||||||
};
|
};
|
||||||
virtual void Check(Status* status);
|
virtual void Check(Status* status);
|
||||||
|
|
||||||
// Selection.
|
|
||||||
// Propagated from Parents to Children.
|
|
||||||
virtual void Select(Box selected_area) {
|
|
||||||
// TODO: Implement this.
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Elements children_;
|
Elements children_;
|
||||||
@ -66,6 +66,7 @@ class Node {
|
|||||||
|
|
||||||
void Render(Screen& screen, const Element& element);
|
void Render(Screen& screen, const Element& element);
|
||||||
void Render(Screen& screen, Node* node);
|
void Render(Screen& screen, Node* node);
|
||||||
|
void Render(Screen& screen, Node* node, Box selection);
|
||||||
|
|
||||||
} // namespace ftxui
|
} // namespace ftxui
|
||||||
|
|
||||||
|
@ -11,13 +11,10 @@ struct Box {
|
|||||||
int x_max = 0;
|
int x_max = 0;
|
||||||
int y_min = 0;
|
int y_min = 0;
|
||||||
int y_max = 0;
|
int y_max = 0;
|
||||||
bool isXInverted = false; // false means the box box from x_min to x_max (in the case of a selection for example)
|
|
||||||
bool isYInverted = false; // false means the box box from y_min to y_max (in the case of a selection for example)
|
|
||||||
|
|
||||||
static auto Intersection(Box a, Box b) -> Box;
|
static auto Intersection(Box a, Box b) -> Box;
|
||||||
static auto Union(Box a, Box b) -> Box;
|
static auto Union(Box a, Box b) -> Box;
|
||||||
bool Contain(int x, int y) const;
|
bool Contain(int x, int y) const;
|
||||||
Box Clean() const;
|
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
bool operator==(const Box& other) const;
|
bool operator==(const Box& other) const;
|
||||||
bool operator!=(const Box& other) const;
|
bool operator!=(const Box& other) const;
|
||||||
|
@ -63,12 +63,6 @@ class Screen : public Image {
|
|||||||
Cursor cursor() const { return cursor_; }
|
Cursor cursor() const { return cursor_; }
|
||||||
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
void SetCursor(Cursor cursor) { cursor_ = cursor; }
|
||||||
|
|
||||||
bool selection_enabled = false;
|
|
||||||
CapturedMouse selection_pending;
|
|
||||||
Box mouse_selection_region;
|
|
||||||
Box selection_region;
|
|
||||||
std::string selection_text;
|
|
||||||
|
|
||||||
// Store an hyperlink in the screen. Return the id of the hyperlink. The id is
|
// Store an hyperlink in the screen. Return the id of the hyperlink. The id is
|
||||||
// used to identify the hyperlink when the user click on it.
|
// used to identify the hyperlink when the user click on it.
|
||||||
uint8_t RegisterHyperlink(const std::string& link);
|
uint8_t RegisterHyperlink(const std::string& link);
|
||||||
|
@ -838,53 +838,36 @@ bool ScreenInteractive::HandleSelection(Event event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mouse.motion == Mouse::Pressed) {
|
if (mouse.motion == Mouse::Pressed) {
|
||||||
selection_pending = CaptureMouse();
|
selection_pending_ = CaptureMouse();
|
||||||
if (!selection_pending) {
|
if (!selection_pending_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
selection_enabled = true;
|
selection_enabled_ = true;
|
||||||
mouse_selection_region.x_min = mouse.x;
|
selection_box_.x_min = mouse.x;
|
||||||
mouse_selection_region.y_min = mouse.y;
|
selection_box_.y_min = mouse.y;
|
||||||
mouse_selection_region.x_max = mouse.x;
|
selection_box_.x_max = mouse.x;
|
||||||
mouse_selection_region.y_max = mouse.y;
|
selection_box_.y_max = mouse.y;
|
||||||
|
|
||||||
selection_region = mouse_selection_region.Clean();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selection_pending) {
|
if (!selection_pending_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mouse.motion == Mouse::Moved) {
|
if (mouse.motion == Mouse::Moved) {
|
||||||
mouse_selection_region.x_max = mouse.x;
|
selection_box_.x_max = mouse.x;
|
||||||
mouse_selection_region.y_max = mouse.y;
|
selection_box_.y_max = mouse.y;
|
||||||
|
|
||||||
selection_region = mouse_selection_region.Clean();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mouse.motion == Mouse::Released) {
|
if (mouse.motion != Mouse::Released) {
|
||||||
mouse_selection_region.x_max = mouse.x;
|
return false;
|
||||||
mouse_selection_region.y_max = mouse.y;
|
|
||||||
selection_pending = nullptr;
|
|
||||||
|
|
||||||
selection_region = mouse_selection_region.Clean();
|
|
||||||
|
|
||||||
if (mouse_selection_region.x_min == mouse_selection_region.x_max &&
|
|
||||||
mouse_selection_region.y_min == mouse_selection_region.y_max) {
|
|
||||||
selection_enabled = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
selection_box_.x_max = mouse.x;
|
||||||
}
|
selection_box_.y_max = mouse.y;
|
||||||
|
selection_pending_ = nullptr;
|
||||||
std::string ScreenInteractive::GetSelection() {
|
return true;
|
||||||
return selection_text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// private
|
// private
|
||||||
@ -955,10 +938,7 @@ void ScreenInteractive::Draw(Component component) {
|
|||||||
#endif
|
#endif
|
||||||
previous_frame_resized_ = resized;
|
previous_frame_resized_ = resized;
|
||||||
|
|
||||||
// Clear selection text.
|
Render(*this, document.get(), selection_box_);
|
||||||
selection_text = "";
|
|
||||||
|
|
||||||
Render(*this, document);
|
|
||||||
|
|
||||||
// Set cursor position for user using tools to insert CJK characters.
|
// Set cursor position for user using tools to insert CJK characters.
|
||||||
{
|
{
|
||||||
|
@ -64,6 +64,30 @@ class HBox : public Node {
|
|||||||
x = box.x_max + 1;
|
x = box.x_max + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Selection(Box selection, std::vector<Box>* selected) override {
|
||||||
|
// If this Node box_ doesn't intersect with the selection, then no
|
||||||
|
// selection.
|
||||||
|
if (Box::Intersection(selection, box_).IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool xmin_satured =
|
||||||
|
selection.y_min < box_.y_min || selection.x_min < box_.x_min;
|
||||||
|
const bool xmax_satured =
|
||||||
|
selection.y_max > box_.y_max || selection.x_max > box_.x_max;
|
||||||
|
|
||||||
|
if (xmin_satured) {
|
||||||
|
selection.x_min = box_.x_min;
|
||||||
|
}
|
||||||
|
if (xmax_satured) {
|
||||||
|
selection.x_max = box_.x_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& child : children_) {
|
||||||
|
child->Selection(selection, selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <iostream>
|
||||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||||
// Use of this source code is governed by the MIT license that can be found in
|
// Use of this source code is governed by the MIT license that can be found in
|
||||||
// the LICENSE file.
|
// the LICENSE file.
|
||||||
@ -27,6 +28,20 @@ void Node::SetBox(Box box) {
|
|||||||
box_ = box;
|
box_ = box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Compute the selection of an element.
|
||||||
|
/// @ingroup dom
|
||||||
|
void Node::Selection(Box selection, std::vector<Box>* selected) {
|
||||||
|
// If this Node box_ doesn't intersect with the selection, then no selection.
|
||||||
|
if (Box::Intersection(selection, box_).IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default we defer the selection to the children.
|
||||||
|
for (auto& child : children_) {
|
||||||
|
child->Selection(selection, selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Display an element on a ftxui::Screen.
|
/// @brief Display an element on a ftxui::Screen.
|
||||||
/// @ingroup dom
|
/// @ingroup dom
|
||||||
void Node::Render(Screen& screen) {
|
void Node::Render(Screen& screen) {
|
||||||
@ -45,12 +60,16 @@ void Node::Check(Status* status) {
|
|||||||
/// @brief Display an element on a ftxui::Screen.
|
/// @brief Display an element on a ftxui::Screen.
|
||||||
/// @ingroup dom
|
/// @ingroup dom
|
||||||
void Render(Screen& screen, const Element& element) {
|
void Render(Screen& screen, const Element& element) {
|
||||||
Render(screen, element.get());
|
Render(screen, element.get(), Box{0, 0, -1, -1});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Display an element on a ftxui::Screen.
|
/// @brief Display an element on a ftxui::Screen.
|
||||||
/// @ingroup dom
|
/// @ingroup dom
|
||||||
void Render(Screen& screen, Node* node) {
|
void Render(Screen& screen, Node* node) {
|
||||||
|
Render(screen, node, Box{0, 0, -1, -1});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render(Screen& screen, Node* node, Box selection) {
|
||||||
Box box;
|
Box box;
|
||||||
box.x_min = 0;
|
box.x_min = 0;
|
||||||
box.y_min = 0;
|
box.y_min = 0;
|
||||||
@ -73,11 +92,15 @@ void Render(Screen& screen, Node* node) {
|
|||||||
node->Check(&status);
|
node->Check(&status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Draw the element.
|
// Step 3: Selection
|
||||||
|
std::vector<Box> selected;
|
||||||
|
node->Selection(selection, &selected);
|
||||||
|
|
||||||
|
// Step 4: Draw the element.
|
||||||
screen.stencil = box;
|
screen.stencil = box;
|
||||||
node->Render(screen);
|
node->Render(screen);
|
||||||
|
|
||||||
// Step 4: Apply shaders
|
// Step 5: Apply shaders
|
||||||
screen.ApplyShader();
|
screen.ApplyShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,29 @@ class Text : public Node {
|
|||||||
requirement_.min_y = 1;
|
requirement_.min_y = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Selection(Box selection, std::vector<Box>* selected) override {
|
||||||
|
if (Box::Intersection(selection, box_).IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool xmin_satured =
|
||||||
|
selection.y_min < box_.y_min || selection.x_min < box_.x_min;
|
||||||
|
const bool xmax_satured =
|
||||||
|
selection.y_max > box_.y_max || selection.x_max > box_.x_max;
|
||||||
|
|
||||||
|
selection_start_ = xmin_satured ? box_.x_min : selection.x_min;
|
||||||
|
selection_end_ = xmax_satured ? box_.x_max : selection.x_max;
|
||||||
|
|
||||||
|
has_selection = true;
|
||||||
|
|
||||||
|
Box out;
|
||||||
|
out.x_min = selection_start_;
|
||||||
|
out.x_max = selection_end_;
|
||||||
|
out.y_min = box_.y_min;
|
||||||
|
out.y_max = box_.y_max;
|
||||||
|
selected->push_back(out);
|
||||||
|
}
|
||||||
|
|
||||||
void Render(Screen& screen) override {
|
void Render(Screen& screen) override {
|
||||||
int x = box_.x_min;
|
int x = box_.x_min;
|
||||||
const int y = box_.y_min;
|
const int y = box_.y_min;
|
||||||
@ -35,55 +58,33 @@ class Text : public Node {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the selection start point
|
|
||||||
int selection_start_x = !screen.selection_region.isXInverted ? screen.selection_region.x_min : screen.selection_region.x_max;
|
|
||||||
int selection_start_y = !screen.selection_region.isYInverted ? screen.selection_region.y_min : screen.selection_region.y_max;
|
|
||||||
bool selectedWidget = false;
|
|
||||||
|
|
||||||
if(box_.Contain(selection_start_x, selection_start_y))
|
|
||||||
{
|
|
||||||
selectedWidget = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& cell : Utf8ToGlyphs(text_)) {
|
for (const auto& cell : Utf8ToGlyphs(text_)) {
|
||||||
if (x > box_.x_max) {
|
if (x > box_.x_max) {
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
if (cell == "\n") {
|
if (cell == "\n") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Pixel ¤tPixel = screen.PixelAt(x, y);
|
screen.PixelAt(x, y).character = cell;
|
||||||
currentPixel.character = cell;
|
|
||||||
|
|
||||||
if((selectedWidget == true) && (currentPixel.selectable == true))
|
|
||||||
{
|
|
||||||
if(screen.selection_region.Contain(x, y)) {
|
|
||||||
currentPixel.inverted ^= true;
|
|
||||||
screen.selection_text += currentPixel.character;
|
|
||||||
}
|
|
||||||
else if((x >= screen.selection_region.x_min) && (x >= screen.selection_region.x_max) &&
|
|
||||||
(y >= screen.selection_region.y_min) && (y < screen.selection_region.y_max))
|
|
||||||
{
|
|
||||||
// Wrap around selection on the right
|
|
||||||
currentPixel.inverted ^= true;
|
|
||||||
screen.selection_text += currentPixel.character;
|
|
||||||
}
|
|
||||||
else if((x <= screen.selection_region.x_min) && (x <= screen.selection_region.x_max) &&
|
|
||||||
(y > screen.selection_region.y_min) && (y <= screen.selection_region.y_max))
|
|
||||||
{
|
|
||||||
// Wrap around selection on the left
|
|
||||||
currentPixel.inverted ^= true;
|
|
||||||
screen.selection_text += currentPixel.character;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
++x;
|
++x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!has_selection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert the selection
|
||||||
|
for(int x = selection_start_; x <= selection_end_; x++) {
|
||||||
|
screen.PixelAt(x, y).inverted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string text_;
|
std::string text_;
|
||||||
|
bool has_selection = false;
|
||||||
|
int selection_start_ = 0;
|
||||||
|
int selection_end_ = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VText : public Node {
|
class VText : public Node {
|
||||||
|
@ -64,6 +64,29 @@ class VBox : public Node {
|
|||||||
y = box.y_max + 1;
|
y = box.y_max + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Selection(Box selection, std::vector<Box>* selected) override {
|
||||||
|
// If this Node box_ doesn't intersect with the selection, then no
|
||||||
|
// selection.
|
||||||
|
if (Box::Intersection(selection, box_).IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool ymin_satured =
|
||||||
|
selection.x_min < box_.x_min || selection.y_min < box_.y_min;
|
||||||
|
const bool ymax_satured =
|
||||||
|
selection.x_max > box_.x_max || selection.y_max > box_.y_max;
|
||||||
|
if (ymin_satured) {
|
||||||
|
selection.y_min = box_.y_min;
|
||||||
|
}
|
||||||
|
if (ymax_satured) {
|
||||||
|
selection.y_max = box_.y_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& child : children_) {
|
||||||
|
child->Selection(selection, selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -39,26 +39,6 @@ bool Box::Contain(int x, int y) const {
|
|||||||
y_max >= y;
|
y_max >= y;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return a copy of box with the x_min <= x_max and y_min <= y_max.
|
|
||||||
/// @ingroup screen
|
|
||||||
Box Box::Clean() const {
|
|
||||||
Box newBox = *this;
|
|
||||||
|
|
||||||
if(newBox.x_min > newBox.x_max)
|
|
||||||
{
|
|
||||||
std::swap(newBox.x_min, newBox.x_max);
|
|
||||||
newBox.isXInverted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(newBox.y_min > newBox.y_max)
|
|
||||||
{
|
|
||||||
std::swap(newBox.y_min, newBox.y_max);
|
|
||||||
newBox.isYInverted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @return whether the box is empty.
|
/// @return whether the box is empty.
|
||||||
/// @ingroup screen
|
/// @ingroup screen
|
||||||
bool Box::IsEmpty() const {
|
bool Box::IsEmpty() const {
|
||||||
|
@ -390,8 +390,7 @@ Screen Screen::Create(Dimensions dimension) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Screen::Screen(int dimx, int dimy) :
|
Screen::Screen(int dimx, int dimy) :
|
||||||
Image{dimx, dimy},
|
Image{dimx, dimy} {
|
||||||
selection_text("") {
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
// The placement of this call is a bit weird, however we can assume that
|
// The placement of this call is a bit weird, however we can assume that
|
||||||
// anybody who instantiates a Screen object eventually wants to output
|
// anybody who instantiates a Screen object eventually wants to output
|
||||||
|
Loading…
Reference in New Issue
Block a user