2021-12-12 00:58:25 +08:00
|
|
|
#include <algorithm> // for find_if
|
2021-07-15 21:29:33 +08:00
|
|
|
#include <cassert> // for assert
|
2022-03-31 08:17:43 +08:00
|
|
|
#include <cstddef> // for size_t
|
2021-05-15 04:00:49 +08:00
|
|
|
#include <iterator> // for begin, end
|
|
|
|
#include <utility> // for move
|
2021-08-09 05:25:20 +08:00
|
|
|
#include <vector> // for vector, __alloc_traits<>::value_type
|
2020-03-23 05:32:44 +08:00
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
|
|
|
|
#include "ftxui/component/component.hpp"
|
2021-12-12 00:58:25 +08:00
|
|
|
#include "ftxui/component/component_base.hpp" // for ComponentBase, Components
|
2021-05-10 02:32:27 +08:00
|
|
|
#include "ftxui/component/event.hpp" // for Event
|
|
|
|
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
|
|
|
|
#include "ftxui/dom/elements.hpp" // for text, Element
|
2021-05-02 02:40:35 +08:00
|
|
|
|
2022-03-14 01:51:46 +08:00
|
|
|
namespace ftxui::animation {
|
|
|
|
class Params;
|
|
|
|
} // namespace ftxui::animation
|
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
namespace ftxui {
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2021-05-02 02:40:35 +08:00
|
|
|
namespace {
|
2021-07-17 18:02:08 +08:00
|
|
|
class CaptureMouseImpl : public CapturedMouseInterface {};
|
2021-05-10 02:32:27 +08:00
|
|
|
} // namespace
|
2021-05-02 02:40:35 +08:00
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
ComponentBase::~ComponentBase() {
|
2021-07-21 06:37:44 +08:00
|
|
|
DetachAllChildren();
|
2019-01-13 01:24:46 +08:00
|
|
|
}
|
2018-10-10 01:06:03 +08:00
|
|
|
|
2021-05-10 02:32:27 +08:00
|
|
|
/// @brief Return the parent ComponentBase, or nul if any.
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @see Detach
|
2020-09-06 19:46:56 +08:00
|
|
|
/// @see Parent
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @ingroup component
|
2021-07-21 06:37:44 +08:00
|
|
|
ComponentBase* ComponentBase::Parent() const {
|
2020-08-16 08:24:50 +08:00
|
|
|
return parent_;
|
|
|
|
}
|
|
|
|
|
2021-07-15 21:29:33 +08:00
|
|
|
/// @brief Access the child at index `i`.
|
|
|
|
/// @ingroup component
|
|
|
|
Component& ComponentBase::ChildAt(size_t i) {
|
2022-03-31 08:17:43 +08:00
|
|
|
assert(i < ChildCount()); // NOLINT
|
2021-07-15 21:29:33 +08:00
|
|
|
return children_[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Returns the number of children.
|
|
|
|
/// @ingroup component
|
|
|
|
size_t ComponentBase::ChildCount() const {
|
|
|
|
return children_.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Add a child.
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @@param child The child to be attached.
|
|
|
|
/// @ingroup component
|
2021-05-10 02:32:27 +08:00
|
|
|
void ComponentBase::Add(Component child) {
|
|
|
|
child->Detach();
|
|
|
|
child->parent_ = this;
|
|
|
|
children_.push_back(std::move(child));
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2021-07-15 21:29:33 +08:00
|
|
|
/// @brief Detach this child from its parent.
|
|
|
|
/// @see Detach
|
|
|
|
/// @see Parent
|
|
|
|
/// @ingroup component
|
|
|
|
void ComponentBase::Detach() {
|
2022-03-31 08:17:43 +08:00
|
|
|
if (parent_ == nullptr) {
|
2021-07-15 21:29:33 +08:00
|
|
|
return;
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2021-07-15 21:29:33 +08:00
|
|
|
auto it = std::find_if(std::begin(parent_->children_), //
|
|
|
|
std::end(parent_->children_), //
|
|
|
|
[this](const Component& that) { //
|
|
|
|
return this == that.get();
|
|
|
|
});
|
2022-01-12 06:06:36 +08:00
|
|
|
ComponentBase* parent = parent_;
|
2021-07-15 21:29:33 +08:00
|
|
|
parent_ = nullptr;
|
2022-01-12 06:06:36 +08:00
|
|
|
parent->children_.erase(it); // Might delete |this|.
|
2021-07-15 21:29:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// @brief Remove all children.
|
|
|
|
/// @ingroup component
|
|
|
|
void ComponentBase::DetachAllChildren() {
|
2022-03-31 08:17:43 +08:00
|
|
|
while (!children_.empty()) {
|
2021-07-15 21:29:33 +08:00
|
|
|
children_[0]->Detach();
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2021-07-15 21:29:33 +08:00
|
|
|
}
|
|
|
|
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @brief Draw the component.
|
|
|
|
/// Build a ftxui::Element to be drawn on the ftxi::Screen representing this
|
2021-05-10 02:32:27 +08:00
|
|
|
/// ftxui::ComponentBase.
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @ingroup component
|
2021-05-10 02:32:27 +08:00
|
|
|
Element ComponentBase::Render() {
|
2022-03-31 08:17:43 +08:00
|
|
|
if (children_.size() == 1) {
|
2020-08-16 08:24:50 +08:00
|
|
|
return children_.front()->Render();
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2020-08-16 08:24:50 +08:00
|
|
|
|
2021-08-09 05:25:20 +08:00
|
|
|
return text("Not implemented component");
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @brief Called in response to an event.
|
|
|
|
/// @param event The event.
|
|
|
|
/// @return True when the event has been handled.
|
|
|
|
/// The default implementation called OnEvent on every child until one return
|
|
|
|
/// true. If none returns true, return false.
|
|
|
|
/// @ingroup component
|
2022-03-31 08:17:43 +08:00
|
|
|
bool ComponentBase::OnEvent(Event event) { // NOLINT
|
|
|
|
for (Component& child : children_) { // NOLINT
|
|
|
|
if (child->OnEvent(event)) {
|
2018-10-10 01:06:03 +08:00
|
|
|
return true;
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-03-14 01:51:46 +08:00
|
|
|
/// @brief Called in response to an animation event.
|
2023-03-31 23:13:48 +08:00
|
|
|
/// @param params the parameters of the animation
|
2022-03-14 01:51:46 +08:00
|
|
|
/// The default implementation dispatch the event to every child.
|
|
|
|
/// @ingroup component
|
|
|
|
void ComponentBase::OnAnimation(animation::Params& params) {
|
2022-12-20 01:51:25 +08:00
|
|
|
for (const Component& child : children_) {
|
2022-03-14 01:51:46 +08:00
|
|
|
child->OnAnimation(params);
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2022-03-14 01:51:46 +08:00
|
|
|
}
|
|
|
|
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @brief Return the currently Active child.
|
|
|
|
/// @return the currently Active child.
|
|
|
|
/// @ingroup component
|
2021-05-10 02:32:27 +08:00
|
|
|
Component ComponentBase::ActiveChild() {
|
2022-02-19 18:49:12 +08:00
|
|
|
for (auto& child : children_) {
|
2022-03-31 08:17:43 +08:00
|
|
|
if (child->Focusable()) {
|
2022-02-19 18:49:12 +08:00
|
|
|
return child;
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2022-02-19 18:49:12 +08:00
|
|
|
}
|
|
|
|
return nullptr;
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2021-08-06 04:40:40 +08:00
|
|
|
/// @brief Return true when the component contains focusable elements.
|
|
|
|
/// The non focusable Components will be skipped when navigating using the
|
|
|
|
/// keyboard.
|
|
|
|
/// @ingroup component
|
|
|
|
bool ComponentBase::Focusable() const {
|
2022-03-31 08:17:43 +08:00
|
|
|
for (const Component& child : children_) { // NOLINT
|
|
|
|
if (child->Focusable()) {
|
2021-08-06 04:40:40 +08:00
|
|
|
return true;
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2021-08-06 04:40:40 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-26 20:57:42 +08:00
|
|
|
/// @brief Returns if the element if the currently active child of its parent.
|
|
|
|
/// @ingroup component
|
2021-07-21 06:37:44 +08:00
|
|
|
bool ComponentBase::Active() const {
|
2022-03-31 08:17:43 +08:00
|
|
|
return parent_ == nullptr || parent_->ActiveChild().get() == this;
|
2020-08-26 20:57:42 +08:00
|
|
|
}
|
|
|
|
|
2020-08-16 08:24:50 +08:00
|
|
|
/// @brief Returns if the elements if focused by the user.
|
2021-05-10 02:32:27 +08:00
|
|
|
/// True when the ComponentBase is focused by the user. An element is Focused
|
2022-02-19 18:49:12 +08:00
|
|
|
/// when it is with all its ancestors the ActiveChild() of their parents, and it
|
|
|
|
/// Focusable().
|
2020-08-26 20:57:42 +08:00
|
|
|
/// @ingroup component
|
2021-07-21 06:37:44 +08:00
|
|
|
bool ComponentBase::Focused() const {
|
2022-03-31 08:17:43 +08:00
|
|
|
const auto* current = this;
|
2021-07-21 06:37:44 +08:00
|
|
|
while (current && current->Active()) {
|
|
|
|
current = current->parent_;
|
2019-01-13 01:24:46 +08:00
|
|
|
}
|
2022-02-19 18:49:12 +08:00
|
|
|
return !current && Focusable();
|
2018-10-10 01:06:03 +08:00
|
|
|
}
|
|
|
|
|
2020-08-26 20:57:42 +08:00
|
|
|
/// @brief Make the |child| to be the "active" one.
|
2021-07-10 20:23:46 +08:00
|
|
|
/// @param child the child to become active.
|
2020-08-26 20:57:42 +08:00
|
|
|
/// @ingroup component
|
2023-03-31 23:13:48 +08:00
|
|
|
void ComponentBase::SetActiveChild([[maybe_unused]] ComponentBase* child) {}
|
2021-05-10 02:32:27 +08:00
|
|
|
|
|
|
|
/// @brief Make the |child| to be the "active" one.
|
2021-07-10 20:23:46 +08:00
|
|
|
/// @param child the child to become active.
|
2021-05-10 02:32:27 +08:00
|
|
|
/// @ingroup component
|
2022-03-31 08:17:43 +08:00
|
|
|
void ComponentBase::SetActiveChild(Component child) { // NOLINT
|
2021-05-10 02:32:27 +08:00
|
|
|
SetActiveChild(child.get());
|
|
|
|
}
|
2020-08-26 20:57:42 +08:00
|
|
|
|
|
|
|
/// @brief Configure all the ancestors to give focus to this component.
|
|
|
|
/// @ingroup component
|
2021-05-10 02:32:27 +08:00
|
|
|
void ComponentBase::TakeFocus() {
|
|
|
|
ComponentBase* child = this;
|
2021-07-21 06:37:44 +08:00
|
|
|
while (ComponentBase* parent = child->parent_) {
|
2020-08-26 20:57:42 +08:00
|
|
|
parent->SetActiveChild(child);
|
|
|
|
child = parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-02 02:40:35 +08:00
|
|
|
/// @brief Take the CapturedMouse if available. There is only one component of
|
|
|
|
/// them. It represents a component taking priority over others.
|
2023-03-31 23:13:48 +08:00
|
|
|
/// @param event The event
|
2021-05-02 02:40:35 +08:00
|
|
|
/// @ingroup component
|
2022-03-31 08:17:43 +08:00
|
|
|
CapturedMouse ComponentBase::CaptureMouse(const Event& event) { // NOLINT
|
|
|
|
if (event.screen_) {
|
2021-07-17 18:02:08 +08:00
|
|
|
return event.screen_->CaptureMouse();
|
2022-03-31 08:17:43 +08:00
|
|
|
}
|
2021-07-17 18:02:08 +08:00
|
|
|
return std::make_unique<CaptureMouseImpl>();
|
2021-05-02 02:40:35 +08:00
|
|
|
}
|
|
|
|
|
2019-01-12 22:00:08 +08:00
|
|
|
} // namespace ftxui
|
2020-08-16 06:24:18 +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.
|