2023-08-19 19:56:36 +08:00
|
|
|
// 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.
|
2021-05-15 04:00:49 +08:00
|
|
|
#include <algorithm> // for max, min
|
2021-12-12 00:58:25 +08:00
|
|
|
#include <memory> // for make_shared, __shared_ptr_access
|
2021-05-15 04:00:49 +08:00
|
|
|
#include <utility> // for move
|
2020-03-23 05:32:44 +08:00
|
|
|
|
2021-12-12 00:58:25 +08:00
|
|
|
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, focus, frame, select, xframe, yframe
|
2025-03-19 22:33:05 +08:00
|
|
|
#include "ftxui/dom/node.hpp" // for Node, Elements
|
|
|
|
#include "ftxui/dom/requirement.hpp" // for Requirement
|
|
|
|
#include "ftxui/screen/box.hpp" // for Box
|
|
|
|
#include "ftxui/screen/screen.hpp" // for Screen, Screen::Cursor
|
|
|
|
#include "ftxui/util/autoreset.hpp" // for AutoReset
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
namespace ftxui {
|
2019-01-07 00:10:35 +08:00
|
|
|
|
2023-08-19 20:56:28 +08:00
|
|
|
namespace {
|
2025-03-19 22:33:05 +08:00
|
|
|
class Focus : public Node {
|
2019-01-20 05:06:05 +08:00
|
|
|
public:
|
2025-03-19 22:33:05 +08:00
|
|
|
explicit Focus(Elements children) : Node(std::move(children)) {}
|
2019-01-20 05:06:05 +08:00
|
|
|
|
|
|
|
void ComputeRequirement() override {
|
|
|
|
Node::ComputeRequirement();
|
2021-05-16 23:18:11 +08:00
|
|
|
requirement_ = children_[0]->requirement();
|
2025-03-19 22:33:05 +08:00
|
|
|
requirement_.focused.enabled = true;
|
|
|
|
requirement_.focused.node = this;
|
|
|
|
requirement_.focused.box.x_min = 0;
|
|
|
|
requirement_.focused.box.y_min = 0;
|
|
|
|
requirement_.focused.box.x_max = requirement_.min_x - 1;
|
|
|
|
requirement_.focused.box.y_max = requirement_.min_y - 1;
|
2023-03-26 22:14:43 +08:00
|
|
|
}
|
2019-01-20 05:06:05 +08:00
|
|
|
|
|
|
|
void SetBox(Box box) override {
|
2021-07-20 15:59:47 +08:00
|
|
|
Node::SetBox(box);
|
2021-05-16 23:18:11 +08:00
|
|
|
children_[0]->SetBox(box);
|
2019-01-20 05:06:05 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-10 01:06:03 +08:00
|
|
|
class Frame : public Node {
|
|
|
|
public:
|
2021-07-20 15:59:47 +08:00
|
|
|
Frame(Elements children, bool x_frame, bool y_frame)
|
2020-05-25 07:34:13 +08:00
|
|
|
: Node(std::move(children)), x_frame_(x_frame), y_frame_(y_frame) {}
|
2018-10-10 01:06:03 +08:00
|
|
|
|
|
|
|
void SetBox(Box box) override {
|
|
|
|
Node::SetBox(box);
|
2025-03-19 22:33:05 +08:00
|
|
|
auto& focused_box = requirement_.focused.box;
|
2019-01-20 05:06:05 +08:00
|
|
|
Box children_box = box;
|
|
|
|
|
2020-06-07 21:54:45 +08:00
|
|
|
if (x_frame_) {
|
2022-12-20 01:51:25 +08:00
|
|
|
const int external_dimx = box.x_max - box.x_min;
|
|
|
|
const int internal_dimx = std::max(requirement_.min_x, external_dimx);
|
2025-03-19 22:33:05 +08:00
|
|
|
const int focused_dimx = focused_box.x_max - focused_box.x_min;
|
|
|
|
int dx = focused_box.x_min - external_dimx / 2 + focused_dimx / 2;
|
2020-06-07 21:54:45 +08:00
|
|
|
dx = std::max(0, std::min(internal_dimx - external_dimx - 1, dx));
|
|
|
|
children_box.x_min = box.x_min - dx;
|
|
|
|
children_box.x_max = box.x_min + internal_dimx - dx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y_frame_) {
|
2022-12-20 01:51:25 +08:00
|
|
|
const int external_dimy = box.y_max - box.y_min;
|
|
|
|
const int internal_dimy = std::max(requirement_.min_y, external_dimy);
|
2025-03-19 22:33:05 +08:00
|
|
|
const int focused_dimy = focused_box.y_max - focused_box.y_min;
|
|
|
|
int dy = focused_box.y_min - external_dimy / 2 + focused_dimy / 2;
|
2020-06-07 21:54:45 +08:00
|
|
|
dy = std::max(0, std::min(internal_dimy - external_dimy - 1, dy));
|
|
|
|
children_box.y_min = box.y_min - dy;
|
|
|
|
children_box.y_max = box.y_min + internal_dimy - dy;
|
|
|
|
}
|
2019-01-20 05:06:05 +08:00
|
|
|
|
2021-05-16 23:18:11 +08:00
|
|
|
children_[0]->SetBox(children_box);
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Render(Screen& screen) override {
|
2022-12-20 01:51:25 +08:00
|
|
|
const AutoReset<Box> stencil(&screen.stencil,
|
|
|
|
Box::Intersection(box_, screen.stencil));
|
2021-05-16 23:18:11 +08:00
|
|
|
children_[0]->Render(screen);
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
2020-06-07 21:54:45 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool x_frame_;
|
|
|
|
bool y_frame_;
|
2018-10-10 01:06:03 +08:00
|
|
|
};
|
|
|
|
|
2022-11-11 21:09:53 +08:00
|
|
|
class FocusCursor : public Focus {
|
|
|
|
public:
|
|
|
|
FocusCursor(Elements children, Screen::Cursor::Shape shape)
|
|
|
|
: Focus(std::move(children)), shape_(shape) {}
|
|
|
|
|
|
|
|
private:
|
2025-03-19 22:33:05 +08:00
|
|
|
void ComputeRequirement() override {
|
|
|
|
Focus::ComputeRequirement(); // NOLINT
|
|
|
|
requirement_.focused.cursor_shape = shape_;
|
2022-11-11 21:09:53 +08:00
|
|
|
}
|
|
|
|
Screen::Cursor::Shape shape_;
|
|
|
|
};
|
|
|
|
|
2023-08-19 20:56:28 +08:00
|
|
|
} // namespace
|
|
|
|
|
2025-03-19 22:33:05 +08:00
|
|
|
/// @brief Set the `child` to be the one focused among its siblings.
|
2023-08-19 20:56:28 +08:00
|
|
|
/// @param child The element to be focused.
|
|
|
|
/// @ingroup dom
|
|
|
|
Element focus(Element child) {
|
|
|
|
return std::make_shared<Focus>(unpack(std::move(child)));
|
|
|
|
}
|
|
|
|
|
2025-03-19 22:33:05 +08:00
|
|
|
/// @deprecated Use `focus` instead.
|
|
|
|
/// @brief Set the `child` to be the one focused among its siblings.
|
|
|
|
/// @param child The element to be focused.
|
|
|
|
Element select(Element e) {
|
|
|
|
return focus(std::move(e));
|
|
|
|
}
|
|
|
|
|
2023-08-19 20:56:28 +08:00
|
|
|
/// @brief Allow an element to be displayed inside a 'virtual' area. It size can
|
|
|
|
/// be larger than its container. In this case only a smaller portion is
|
|
|
|
/// displayed. The view is scrollable to make the focused element visible.
|
|
|
|
/// @see frame
|
|
|
|
/// @see xframe
|
|
|
|
/// @see yframe
|
|
|
|
Element frame(Element child) {
|
|
|
|
return std::make_shared<Frame>(unpack(std::move(child)), true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Same as `frame`, but only on the x-axis.
|
|
|
|
/// @see frame
|
|
|
|
/// @see xframe
|
|
|
|
/// @see yframe
|
|
|
|
Element xframe(Element child) {
|
|
|
|
return std::make_shared<Frame>(unpack(std::move(child)), true, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Same as `frame`, but only on the y-axis.
|
|
|
|
/// @see frame
|
|
|
|
/// @see xframe
|
|
|
|
/// @see yframe
|
|
|
|
Element yframe(Element child) {
|
|
|
|
return std::make_shared<Frame>(unpack(std::move(child)), false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a still block.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorBlock(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::Block);
|
|
|
|
}
|
2023-08-19 20:56:28 +08:00
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a blinking block.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorBlockBlinking(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::BlockBlinking);
|
|
|
|
}
|
2023-08-19 20:56:28 +08:00
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a still block.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorBar(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::Bar);
|
|
|
|
}
|
2023-08-19 20:56:28 +08:00
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a blinking bar.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorBarBlinking(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::BarBlinking);
|
|
|
|
}
|
2023-08-19 20:56:28 +08:00
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a still underline.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorUnderline(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::Underline);
|
|
|
|
}
|
2023-08-19 20:56:28 +08:00
|
|
|
|
|
|
|
/// @brief Same as `focus`, but set the cursor shape to be a blinking underline.
|
|
|
|
/// @see focus
|
|
|
|
/// @see focusCursorBlock
|
|
|
|
/// @see focusCursorBlockBlinking
|
|
|
|
/// @see focusCursorBar
|
|
|
|
/// @see focusCursorBarBlinking
|
|
|
|
/// @see focusCursorUnderline
|
|
|
|
/// @see focusCursorUnderlineBlinking
|
|
|
|
/// @ingroup dom
|
2022-11-11 21:09:53 +08:00
|
|
|
Element focusCursorUnderlineBlinking(Element child) {
|
|
|
|
return std::make_shared<FocusCursor>(unpack(std::move(child)),
|
|
|
|
Screen::Cursor::UnderlineBlinking);
|
|
|
|
}
|
|
|
|
|
2020-02-12 04:44:55 +08:00
|
|
|
} // namespace ftxui
|