mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2025-09-17 08:28:09 +08:00
Make component more functionnal
This commit is contained in:
@@ -9,12 +9,47 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element Button::Render() {
|
||||
auto style = Focused() ? inverted : nothing;
|
||||
return text(label) | border | style | reflect(box_);
|
||||
/// @brief Draw a button. Execute a function when clicked.
|
||||
/// @param label The label of the button.
|
||||
/// @param on_click The action to execute when clicked.
|
||||
/// @ingroup component
|
||||
/// @see ButtonBase
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::FitComponent();
|
||||
/// std::wstring label = L"Click to quit";
|
||||
/// Component button = Button(&label, screen.ExitLoopClosure());
|
||||
/// screen.Loop(button)
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// ┌─────────────┐
|
||||
/// │Click to quit│
|
||||
/// └─────────────┘
|
||||
/// ```
|
||||
Component Button(const std::wstring* label, std::function<void()> on_click) {
|
||||
return Make<ButtonBase>(label, on_click);
|
||||
}
|
||||
|
||||
bool Button::OnEvent(Event event) {
|
||||
// static
|
||||
ButtonBase* ButtonBase::From(Component component) {
|
||||
return static_cast<ButtonBase*>(component.get());
|
||||
}
|
||||
|
||||
ButtonBase::ButtonBase(const std::wstring* label,
|
||||
std::function<void()> on_click)
|
||||
: label_(label), on_click_(on_click) {}
|
||||
|
||||
Element ButtonBase::Render() {
|
||||
auto style = Focused() ? inverted : nothing;
|
||||
return text(*label_) | border | style | reflect(box_);
|
||||
}
|
||||
|
||||
bool ButtonBase::OnEvent(Event event) {
|
||||
if (event.is_mouse() && box_.Contain(event.mouse().x, event.mouse().y)) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
@@ -23,7 +58,7 @@ bool Button::OnEvent(Event event) {
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed) {
|
||||
on_click();
|
||||
on_click_();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,7 +66,7 @@ bool Button::OnEvent(Event event) {
|
||||
}
|
||||
|
||||
if (event == Event::Return) {
|
||||
on_click();
|
||||
on_click_();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@@ -9,29 +9,62 @@
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element CheckBox::Render() {
|
||||
/// @brief Draw checkable element.
|
||||
/// @param label The label of the checkbox.
|
||||
/// @param checked Whether the checkbox is checked or not.
|
||||
/// @ingroup component
|
||||
/// @see CheckboxBase
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::FitComponent();
|
||||
/// std::wstring label = L"Make a sandwidth";
|
||||
/// bool checked = false;
|
||||
/// Component checkbox = Checkbox(&label, &checked);
|
||||
/// screen.Loop(checkbox)
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// ☐ Make a sandwitch
|
||||
/// ```
|
||||
Component Checkbox(const std::wstring* label, bool* checked) {
|
||||
return Make<CheckboxBase>(label, checked);
|
||||
}
|
||||
|
||||
// static
|
||||
CheckboxBase* From(Component component) {
|
||||
return static_cast<CheckboxBase*>(component.get());
|
||||
}
|
||||
|
||||
CheckboxBase::CheckboxBase(const std::wstring* label, bool* state)
|
||||
: label_(label), state_(state) {}
|
||||
|
||||
Element CheckboxBase::Render() {
|
||||
bool is_focused = Focused();
|
||||
auto style = is_focused ? focused_style : unfocused_style;
|
||||
auto focus_management = is_focused ? focus : state ? select : nothing;
|
||||
return hbox(text(state ? checked : unchecked),
|
||||
text(label) | style | focus_management) |
|
||||
auto focus_management = is_focused ? focus : *state_ ? select : nothing;
|
||||
return hbox(text(*state_ ? checked : unchecked),
|
||||
text(*label_) | style | focus_management) |
|
||||
reflect(box_);
|
||||
}
|
||||
|
||||
bool CheckBox::OnEvent(Event event) {
|
||||
bool CheckboxBase::OnEvent(Event event) {
|
||||
if (event.is_mouse())
|
||||
return OnMouseEvent(event);
|
||||
|
||||
if (event == Event::Character(' ') || event == Event::Return) {
|
||||
state = !state;
|
||||
*state_ = !*state_;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckBox::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
bool CheckboxBase::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
if (!box_.Contain(event.mouse().x, event.mouse().y))
|
||||
return false;
|
||||
@@ -40,7 +73,7 @@ bool CheckBox::OnMouseEvent(Event event) {
|
||||
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed) {
|
||||
state = !state;
|
||||
*state_ = !*state_;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,10 +1,14 @@
|
||||
#include <algorithm> // for find_if
|
||||
#include <ext/alloc_traits.h> // for __alloc_traits<>::value_type
|
||||
#include <iterator> // for begin, end
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
|
||||
#include "ftxui/component/component.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp"
|
||||
#include "ftxui/component/event.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
|
||||
#include "ftxui/dom/elements.hpp" // for text, Element
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
@@ -13,33 +17,35 @@ class CaptureMouseImpl : public CapturedMouseInterface {
|
||||
public:
|
||||
~CaptureMouseImpl() override {}
|
||||
};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Component::~Component() {
|
||||
ComponentBase::~ComponentBase() {
|
||||
Detach();
|
||||
}
|
||||
|
||||
/// @brief Return the parent Component, or nul if any.
|
||||
/// @brief Return the parent ComponentBase, or nul if any.
|
||||
/// @see Attach
|
||||
/// @see Detach
|
||||
/// @see Parent
|
||||
/// @ingroup component
|
||||
Component* Component::Parent() {
|
||||
ComponentBase* ComponentBase::Parent() {
|
||||
return parent_;
|
||||
}
|
||||
|
||||
/// @brief Add a children.
|
||||
/// @@param child The child to be attached.
|
||||
/// @ingroup component
|
||||
void Component::Add(Component* child) {
|
||||
child->Attach(this);
|
||||
void ComponentBase::Add(Component child) {
|
||||
child->Detach();
|
||||
child->parent_ = this;
|
||||
children_.push_back(std::move(child));
|
||||
}
|
||||
|
||||
/// @brief Draw the component.
|
||||
/// Build a ftxui::Element to be drawn on the ftxi::Screen representing this
|
||||
/// ftxui::Component.
|
||||
/// ftxui::ComponentBase.
|
||||
/// @ingroup component
|
||||
Element Component::Render() {
|
||||
Element ComponentBase::Render() {
|
||||
if (children_.size() == 1)
|
||||
return children_.front()->Render();
|
||||
|
||||
@@ -52,8 +58,8 @@ Element Component::Render() {
|
||||
/// The default implementation called OnEvent on every child until one return
|
||||
/// true. If none returns true, return false.
|
||||
/// @ingroup component
|
||||
bool Component::OnEvent(Event event) {
|
||||
for (Component* child : children_) {
|
||||
bool ComponentBase::OnEvent(Event event) {
|
||||
for (Component& child : children_) {
|
||||
if (child->OnEvent(event))
|
||||
return true;
|
||||
}
|
||||
@@ -63,27 +69,27 @@ bool Component::OnEvent(Event event) {
|
||||
/// @brief Return the currently Active child.
|
||||
/// @return the currently Active child.
|
||||
/// @ingroup component
|
||||
Component* Component::ActiveChild() {
|
||||
Component ComponentBase::ActiveChild() {
|
||||
return children_.empty() ? nullptr : children_.front();
|
||||
}
|
||||
|
||||
/// @brief Returns if the element if the currently active child of its parent.
|
||||
/// @ingroup component
|
||||
bool Component::Active() {
|
||||
return !parent_ || parent_->ActiveChild() == this;
|
||||
bool ComponentBase::Active() {
|
||||
return !parent_ || parent_->ActiveChild().get() == this;
|
||||
}
|
||||
|
||||
/// @brief Returns if the elements if focused by the user.
|
||||
/// True when the Component is focused by the user. An element is Focused when
|
||||
/// it is with all its ancestors the ActiveChild() of their parents.
|
||||
/// True when the ComponentBase is focused by the user. An element is Focused
|
||||
/// when it is with all its ancestors the ActiveChild() of their parents.
|
||||
/// @ingroup component
|
||||
bool Component::Focused() {
|
||||
Component* current = this;
|
||||
bool ComponentBase::Focused() {
|
||||
ComponentBase* current = this;
|
||||
for (;;) {
|
||||
Component* parent = current->parent_;
|
||||
ComponentBase* parent = current->parent_;
|
||||
if (!parent)
|
||||
return true;
|
||||
if (parent->ActiveChild() != current)
|
||||
if (parent->ActiveChild().get() != current)
|
||||
return false;
|
||||
current = parent;
|
||||
}
|
||||
@@ -92,13 +98,20 @@ bool Component::Focused() {
|
||||
/// @brief Make the |child| to be the "active" one.
|
||||
/// @argument child the child to become active.
|
||||
/// @ingroup component
|
||||
void Component::SetActiveChild(Component*) {}
|
||||
void ComponentBase::SetActiveChild(ComponentBase*) {}
|
||||
|
||||
/// @brief Make the |child| to be the "active" one.
|
||||
/// @argument child the child to become active.
|
||||
/// @ingroup component
|
||||
void ComponentBase::SetActiveChild(Component child) {
|
||||
SetActiveChild(child.get());
|
||||
}
|
||||
|
||||
/// @brief Configure all the ancestors to give focus to this component.
|
||||
/// @ingroup component
|
||||
void Component::TakeFocus() {
|
||||
Component* child = this;
|
||||
Component* parent = parent_;
|
||||
void ComponentBase::TakeFocus() {
|
||||
ComponentBase* child = this;
|
||||
ComponentBase* parent = parent_;
|
||||
while (parent) {
|
||||
parent->SetActiveChild(child);
|
||||
child = parent;
|
||||
@@ -110,7 +123,7 @@ void Component::TakeFocus() {
|
||||
/// them. It represents a component taking priority over others.
|
||||
/// @argument event
|
||||
/// @ingroup component
|
||||
CapturedMouse Component::CaptureMouse(const Event& event) {
|
||||
CapturedMouse ComponentBase::CaptureMouse(const Event& event) {
|
||||
if (!event.screen_)
|
||||
return std::make_unique<CaptureMouseImpl>();
|
||||
return event.screen_->CaptureMouse();
|
||||
@@ -121,25 +134,17 @@ CapturedMouse Component::CaptureMouse(const Event& event) {
|
||||
/// @see Detach
|
||||
/// @see Parent
|
||||
/// @ingroup component
|
||||
void Component::Detach() {
|
||||
void ComponentBase::Detach() {
|
||||
if (!parent_)
|
||||
return;
|
||||
auto it = std::find(std::begin(parent_->children_),
|
||||
std::end(parent_->children_), this);
|
||||
auto it = std::find_if(std::begin(parent_->children_), //
|
||||
std::end(parent_->children_), //
|
||||
[this](const Component& that) { //
|
||||
return this == that.get();
|
||||
});
|
||||
parent_->children_.erase(it);
|
||||
}
|
||||
|
||||
/// @brief Attach this element to its parent.
|
||||
/// @see Attach
|
||||
/// @see Detach
|
||||
/// @see Parent
|
||||
/// @ingroup component
|
||||
void Component::Attach(Component* parent) {
|
||||
Detach();
|
||||
parent_ = parent;
|
||||
parent_->children_.push_back(this);
|
||||
}
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@@ -1,33 +1,69 @@
|
||||
#include "ftxui/component/container.hpp"
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <ext/alloc_traits.h> // for __alloc_traits<>::value_type
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, make_shared, __shared_ptr_access<>::element_type, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector, allocator
|
||||
|
||||
#include <stddef.h>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "ftxui/component/container.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Component ContainerVertical(Components children) {
|
||||
return Container::Vertical(std::move(children));
|
||||
}
|
||||
|
||||
Component ContainerHorizontal(Components children) {
|
||||
return Container::Horizontal(std::move(children));
|
||||
}
|
||||
|
||||
Component ContainerTab(int* selector, Components children) {
|
||||
return Container::Tab(selector, std::move(children));
|
||||
}
|
||||
|
||||
// static
|
||||
Container Container::Horizontal() {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::HorizontalEvent;
|
||||
container.render_handler_ = &Container::HorizontalRender;
|
||||
Component Container::Vertical() {
|
||||
return Vertical({});
|
||||
}
|
||||
|
||||
// static
|
||||
Component Container::Vertical(Components children) {
|
||||
auto container = std::make_shared<Container>();
|
||||
container->event_handler_ = &Container::VerticalEvent;
|
||||
container->render_handler_ = &Container::VerticalRender;
|
||||
for (Component& child : children)
|
||||
container->Add(std::move(child));
|
||||
return container;
|
||||
}
|
||||
|
||||
// static
|
||||
Container Container::Vertical() {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::VerticalEvent;
|
||||
container.render_handler_ = &Container::VerticalRender;
|
||||
Component Container::Horizontal() {
|
||||
return Horizontal({});
|
||||
}
|
||||
|
||||
// static
|
||||
Component Container::Horizontal(Components children) {
|
||||
auto container = std::make_shared<Container>();
|
||||
container->event_handler_ = &Container::HorizontalEvent;
|
||||
container->render_handler_ = &Container::HorizontalRender;
|
||||
for (Component& child : children)
|
||||
container->Add(std::move(child));
|
||||
return container;
|
||||
}
|
||||
|
||||
// static
|
||||
Container Container::Tab(int* selector) {
|
||||
Container container;
|
||||
container.event_handler_ = &Container::TabEvent;
|
||||
container.render_handler_ = &Container::TabRender;
|
||||
container.selector_ = selector;
|
||||
Component Container::Tab(int* selector) {
|
||||
return Tab(selector, {});
|
||||
}
|
||||
|
||||
// static
|
||||
Component Container::Tab(int* selector, Components children) {
|
||||
auto container = std::make_shared<Container>();
|
||||
container->selector_ = selector;
|
||||
container->event_handler_ = &Container::TabEvent;
|
||||
container->render_handler_ = &Container::TabRender;
|
||||
for (Component& child : children)
|
||||
container->Add(std::move(child));
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -44,7 +80,7 @@ bool Container::OnEvent(Event event) {
|
||||
return (this->*event_handler_)(event);
|
||||
}
|
||||
|
||||
Component* Container::ActiveChild() {
|
||||
Component Container::ActiveChild() {
|
||||
if (children_.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
@@ -52,9 +88,9 @@ Component* Container::ActiveChild() {
|
||||
return children_[selected % children_.size()];
|
||||
}
|
||||
|
||||
void Container::SetActiveChild(Component* child) {
|
||||
void Container::SetActiveChild(ComponentBase* child) {
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
if (children_[i] == child) {
|
||||
if (children_[i].get() == child) {
|
||||
(selector_ ? *selector_ : selected_) = i;
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +150,7 @@ Element Container::HorizontalRender() {
|
||||
}
|
||||
|
||||
Element Container::TabRender() {
|
||||
Component* active_child = ActiveChild();
|
||||
Component active_child = ActiveChild();
|
||||
if (active_child)
|
||||
return active_child->Render();
|
||||
return text(L"Empty container");
|
||||
@@ -124,7 +160,7 @@ bool Container::OnMouseEvent(Event event) {
|
||||
if (selector_)
|
||||
return ActiveChild()->OnEvent(event);
|
||||
|
||||
for (Component* child : children_) {
|
||||
for (Component& child : children_) {
|
||||
if (child->OnEvent(event))
|
||||
return true;
|
||||
}
|
||||
|
@@ -1,205 +1,205 @@
|
||||
#include <gtest/gtest-message.h> // for Message
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver
|
||||
#include <memory> // for allocator
|
||||
#include <gtest/gtest-message.h> // for Message
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr, allocator
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/container.hpp"
|
||||
#include "ftxui/screen/box.hpp" // for ftxui
|
||||
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, EXPEC...
|
||||
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, EXPECT_FALSE, EXPECT_TRUE, Test, TEST
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
TEST(ContainerTest, HorizontalEvent) {
|
||||
auto container = Container::Horizontal();
|
||||
Component c0, c1, c2;
|
||||
container.Add(&c0);
|
||||
container.Add(&c1);
|
||||
container.Add(&c2);
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(c2);
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With arrow key in the wrong dimension.
|
||||
container.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container->OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With vim like characters.
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With vim like characters in the wrong direction.
|
||||
container.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container->OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With tab characters.
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
}
|
||||
|
||||
TEST(ContainerTest, VerticalEvent) {
|
||||
auto container = Container::Vertical();
|
||||
Component c0, c1, c2;
|
||||
container.Add(&c0);
|
||||
container.Add(&c1);
|
||||
container.Add(&c2);
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(c2);
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With arrow key in the wrong dimension.
|
||||
container.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With vim like characters.
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With vim like characters in the wrong direction.
|
||||
container.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
|
||||
// With tab characters.
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
container.OnEvent(Event::TabReverse);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
container->OnEvent(Event::TabReverse);
|
||||
}
|
||||
|
||||
TEST(ContainerTest, SetActiveChild) {
|
||||
auto container = Container::Horizontal();
|
||||
Component c0, c1, c2;
|
||||
container.Add(&c0);
|
||||
container.Add(&c1);
|
||||
container.Add(&c2);
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(c2);
|
||||
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_TRUE(c0.Focused());
|
||||
EXPECT_TRUE(c0.Active());
|
||||
EXPECT_FALSE(c1.Focused());
|
||||
EXPECT_FALSE(c1.Active());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
EXPECT_TRUE(c0->Focused());
|
||||
EXPECT_TRUE(c0->Active());
|
||||
EXPECT_FALSE(c1->Focused());
|
||||
EXPECT_FALSE(c1->Active());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
|
||||
container.SetActiveChild(&c0);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_TRUE(c0.Focused());
|
||||
EXPECT_TRUE(c0.Active());
|
||||
EXPECT_FALSE(c1.Focused());
|
||||
EXPECT_FALSE(c1.Active());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
container->SetActiveChild(c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
EXPECT_TRUE(c0->Focused());
|
||||
EXPECT_TRUE(c0->Active());
|
||||
EXPECT_FALSE(c1->Focused());
|
||||
EXPECT_FALSE(c1->Active());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
|
||||
container.SetActiveChild(&c1);
|
||||
EXPECT_EQ(container.ActiveChild(), &c1);
|
||||
EXPECT_FALSE(c0.Focused());
|
||||
EXPECT_FALSE(c0.Active());
|
||||
EXPECT_TRUE(c1.Focused());
|
||||
EXPECT_TRUE(c1.Active());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
container->SetActiveChild(c1);
|
||||
EXPECT_EQ(container->ActiveChild(), c1);
|
||||
EXPECT_FALSE(c0->Focused());
|
||||
EXPECT_FALSE(c0->Active());
|
||||
EXPECT_TRUE(c1->Focused());
|
||||
EXPECT_TRUE(c1->Active());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
|
||||
container.SetActiveChild(&c2);
|
||||
EXPECT_EQ(container.ActiveChild(), &c2);
|
||||
EXPECT_FALSE(c0.Focused());
|
||||
EXPECT_FALSE(c0.Active());
|
||||
EXPECT_FALSE(c1.Focused());
|
||||
EXPECT_FALSE(c1.Active());
|
||||
EXPECT_TRUE(c2.Focused());
|
||||
EXPECT_TRUE(c2.Active());
|
||||
container->SetActiveChild(c2);
|
||||
EXPECT_EQ(container->ActiveChild(), c2);
|
||||
EXPECT_FALSE(c0->Focused());
|
||||
EXPECT_FALSE(c0->Active());
|
||||
EXPECT_FALSE(c1->Focused());
|
||||
EXPECT_FALSE(c1->Active());
|
||||
EXPECT_TRUE(c2->Focused());
|
||||
EXPECT_TRUE(c2->Active());
|
||||
|
||||
container.SetActiveChild(&c0);
|
||||
EXPECT_EQ(container.ActiveChild(), &c0);
|
||||
EXPECT_TRUE(c0.Focused());
|
||||
EXPECT_TRUE(c0.Active());
|
||||
EXPECT_FALSE(c1.Focused());
|
||||
EXPECT_FALSE(c1.Active());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
container->SetActiveChild(c0);
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
EXPECT_TRUE(c0->Focused());
|
||||
EXPECT_TRUE(c0->Active());
|
||||
EXPECT_FALSE(c1->Focused());
|
||||
EXPECT_FALSE(c1->Active());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
}
|
||||
|
||||
TEST(ContainerTest, TakeFocus) {
|
||||
@@ -214,74 +214,74 @@ TEST(ContainerTest, TakeFocus) {
|
||||
auto c22 = Container::Horizontal();
|
||||
auto c23 = Container::Horizontal();
|
||||
|
||||
c.Add(&c1);
|
||||
c.Add(&c2);
|
||||
c.Add(&c3);
|
||||
c1.Add(&c11);
|
||||
c1.Add(&c12);
|
||||
c1.Add(&c13);
|
||||
c2.Add(&c21);
|
||||
c2.Add(&c22);
|
||||
c2.Add(&c23);
|
||||
c->Add(c1);
|
||||
c->Add(c2);
|
||||
c->Add(c3);
|
||||
c1->Add(c11);
|
||||
c1->Add(c12);
|
||||
c1->Add(c13);
|
||||
c2->Add(c21);
|
||||
c2->Add(c22);
|
||||
c2->Add(c23);
|
||||
|
||||
EXPECT_TRUE(c.Focused());
|
||||
EXPECT_TRUE(c1.Focused());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_TRUE(c11.Focused());
|
||||
EXPECT_FALSE(c12.Focused());
|
||||
EXPECT_FALSE(c13.Focused());
|
||||
EXPECT_FALSE(c21.Focused());
|
||||
EXPECT_FALSE(c22.Focused());
|
||||
EXPECT_FALSE(c23.Focused());
|
||||
EXPECT_TRUE(c.Active());
|
||||
EXPECT_TRUE(c1.Active());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
EXPECT_TRUE(c11.Active());
|
||||
EXPECT_FALSE(c12.Active());
|
||||
EXPECT_FALSE(c13.Active());
|
||||
EXPECT_TRUE(c21.Active());
|
||||
EXPECT_FALSE(c22.Active());
|
||||
EXPECT_FALSE(c23.Active());
|
||||
EXPECT_TRUE(c->Focused());
|
||||
EXPECT_TRUE(c1->Focused());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_TRUE(c11->Focused());
|
||||
EXPECT_FALSE(c12->Focused());
|
||||
EXPECT_FALSE(c13->Focused());
|
||||
EXPECT_FALSE(c21->Focused());
|
||||
EXPECT_FALSE(c22->Focused());
|
||||
EXPECT_FALSE(c23->Focused());
|
||||
EXPECT_TRUE(c->Active());
|
||||
EXPECT_TRUE(c1->Active());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
EXPECT_TRUE(c11->Active());
|
||||
EXPECT_FALSE(c12->Active());
|
||||
EXPECT_FALSE(c13->Active());
|
||||
EXPECT_TRUE(c21->Active());
|
||||
EXPECT_FALSE(c22->Active());
|
||||
EXPECT_FALSE(c23->Active());
|
||||
|
||||
c22.TakeFocus();
|
||||
EXPECT_TRUE(c.Focused());
|
||||
EXPECT_FALSE(c1.Focused());
|
||||
EXPECT_TRUE(c2.Focused());
|
||||
EXPECT_FALSE(c11.Focused());
|
||||
EXPECT_FALSE(c12.Focused());
|
||||
EXPECT_FALSE(c13.Focused());
|
||||
EXPECT_FALSE(c21.Focused());
|
||||
EXPECT_TRUE(c22.Focused());
|
||||
EXPECT_FALSE(c23.Focused());
|
||||
EXPECT_TRUE(c.Active());
|
||||
EXPECT_FALSE(c1.Active());
|
||||
EXPECT_TRUE(c2.Active());
|
||||
EXPECT_TRUE(c11.Active());
|
||||
EXPECT_FALSE(c12.Active());
|
||||
EXPECT_FALSE(c13.Active());
|
||||
EXPECT_FALSE(c21.Active());
|
||||
EXPECT_TRUE(c22.Active());
|
||||
EXPECT_FALSE(c23.Active());
|
||||
c22->TakeFocus();
|
||||
EXPECT_TRUE(c->Focused());
|
||||
EXPECT_FALSE(c1->Focused());
|
||||
EXPECT_TRUE(c2->Focused());
|
||||
EXPECT_FALSE(c11->Focused());
|
||||
EXPECT_FALSE(c12->Focused());
|
||||
EXPECT_FALSE(c13->Focused());
|
||||
EXPECT_FALSE(c21->Focused());
|
||||
EXPECT_TRUE(c22->Focused());
|
||||
EXPECT_FALSE(c23->Focused());
|
||||
EXPECT_TRUE(c->Active());
|
||||
EXPECT_FALSE(c1->Active());
|
||||
EXPECT_TRUE(c2->Active());
|
||||
EXPECT_TRUE(c11->Active());
|
||||
EXPECT_FALSE(c12->Active());
|
||||
EXPECT_FALSE(c13->Active());
|
||||
EXPECT_FALSE(c21->Active());
|
||||
EXPECT_TRUE(c22->Active());
|
||||
EXPECT_FALSE(c23->Active());
|
||||
|
||||
c1.TakeFocus();
|
||||
EXPECT_TRUE(c.Focused());
|
||||
EXPECT_TRUE(c1.Focused());
|
||||
EXPECT_FALSE(c2.Focused());
|
||||
EXPECT_TRUE(c11.Focused());
|
||||
EXPECT_FALSE(c12.Focused());
|
||||
EXPECT_FALSE(c13.Focused());
|
||||
EXPECT_FALSE(c21.Focused());
|
||||
EXPECT_FALSE(c22.Focused());
|
||||
EXPECT_FALSE(c23.Focused());
|
||||
EXPECT_TRUE(c.Active());
|
||||
EXPECT_TRUE(c1.Active());
|
||||
EXPECT_FALSE(c2.Active());
|
||||
EXPECT_TRUE(c11.Active());
|
||||
EXPECT_FALSE(c12.Active());
|
||||
EXPECT_FALSE(c13.Active());
|
||||
EXPECT_FALSE(c21.Active());
|
||||
EXPECT_TRUE(c22.Active());
|
||||
EXPECT_FALSE(c23.Active());
|
||||
c1->TakeFocus();
|
||||
EXPECT_TRUE(c->Focused());
|
||||
EXPECT_TRUE(c1->Focused());
|
||||
EXPECT_FALSE(c2->Focused());
|
||||
EXPECT_TRUE(c11->Focused());
|
||||
EXPECT_FALSE(c12->Focused());
|
||||
EXPECT_FALSE(c13->Focused());
|
||||
EXPECT_FALSE(c21->Focused());
|
||||
EXPECT_FALSE(c22->Focused());
|
||||
EXPECT_FALSE(c23->Focused());
|
||||
EXPECT_TRUE(c->Active());
|
||||
EXPECT_TRUE(c1->Active());
|
||||
EXPECT_FALSE(c2->Active());
|
||||
EXPECT_TRUE(c11->Active());
|
||||
EXPECT_FALSE(c12->Active());
|
||||
EXPECT_FALSE(c13->Active());
|
||||
EXPECT_FALSE(c21->Active());
|
||||
EXPECT_TRUE(c22->Active());
|
||||
EXPECT_FALSE(c23->Active());
|
||||
}
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#include "ftxui/component/event.hpp"
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/mouse.hpp"
|
||||
#include "ftxui/screen/string.hpp"
|
||||
#include "ftxui/component/event.hpp"
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/screen/string.hpp" // for to_wstring
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
|
@@ -1,39 +1,73 @@
|
||||
#include <algorithm> // for max, min
|
||||
#include <memory> // for shared_ptr
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
|
||||
#include "ftxui/component/input.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp"
|
||||
#include "ftxui/component/mouse.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
/// @brief An input box for editing text.
|
||||
/// @param content The editable content.
|
||||
/// @param placeholder The text displayed when content is still empty.
|
||||
/// @ingroup component
|
||||
/// @see InputBase
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::FitComponent();
|
||||
/// std::wstring content= L"";
|
||||
/// std::wstring placeholder = L"placeholder";
|
||||
/// Component input = Input(&content, &placeholder);
|
||||
/// screen.Loop(input);
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// placeholder
|
||||
/// ```
|
||||
Component Input(std::wstring* content, const std::wstring* placeholder) {
|
||||
return Make<InputBase>(content, placeholder);
|
||||
}
|
||||
|
||||
// static
|
||||
InputBase* InputBase::From(Component component) {
|
||||
return static_cast<InputBase*>(component.get());
|
||||
}
|
||||
|
||||
InputBase::InputBase(std::wstring* content, const std::wstring* placeholder)
|
||||
: content_(content), placeholder_(placeholder) {}
|
||||
|
||||
// Component implementation.
|
||||
Element Input::Render() {
|
||||
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position));
|
||||
Element InputBase::Render() {
|
||||
cursor_position =
|
||||
std::max(0, std::min<int>(content_->size(), cursor_position));
|
||||
auto main_decorator = flex | size(HEIGHT, EQUAL, 1);
|
||||
bool is_focused = Focused();
|
||||
|
||||
// Placeholder.
|
||||
if (content.size() == 0) {
|
||||
// placeholder.
|
||||
if (content_->size() == 0) {
|
||||
if (is_focused)
|
||||
return text(placeholder) | focus | dim | inverted | main_decorator |
|
||||
return text(*placeholder_) | focus | dim | inverted | main_decorator |
|
||||
reflect(input_box_);
|
||||
else
|
||||
return text(placeholder) | dim | main_decorator | reflect(input_box_);
|
||||
return text(*placeholder_) | dim | main_decorator | reflect(input_box_);
|
||||
}
|
||||
|
||||
// Not focused.
|
||||
if (!is_focused)
|
||||
return text(content) | main_decorator | reflect(input_box_);
|
||||
return text(*content_) | main_decorator | reflect(input_box_);
|
||||
|
||||
std::wstring part_before_cursor = content.substr(0, cursor_position);
|
||||
std::wstring part_at_cursor = cursor_position < (int)content.size()
|
||||
? content.substr(cursor_position, 1)
|
||||
std::wstring part_before_cursor = content_->substr(0, cursor_position);
|
||||
std::wstring part_at_cursor = cursor_position < (int)content_->size()
|
||||
? content_->substr(cursor_position, 1)
|
||||
: L" ";
|
||||
std::wstring part_after_cursor = cursor_position < (int)content.size() - 1
|
||||
? content.substr(cursor_position + 1)
|
||||
std::wstring part_after_cursor = cursor_position < (int)content_->size() - 1
|
||||
? content_->substr(cursor_position + 1)
|
||||
: L"";
|
||||
auto focused = is_focused ? focus : select;
|
||||
|
||||
@@ -47,8 +81,9 @@ Element Input::Render() {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
bool Input::OnEvent(Event event) {
|
||||
cursor_position = std::max(0, std::min<int>(content.size(), cursor_position));
|
||||
bool InputBase::OnEvent(Event event) {
|
||||
cursor_position =
|
||||
std::max(0, std::min<int>(content_->size(), cursor_position));
|
||||
|
||||
if (event.is_mouse())
|
||||
return OnMouseEvent(event);
|
||||
@@ -59,7 +94,7 @@ bool Input::OnEvent(Event event) {
|
||||
if (event == Event::Backspace) {
|
||||
if (cursor_position == 0)
|
||||
return false;
|
||||
content.erase(cursor_position - 1, 1);
|
||||
content_->erase(cursor_position - 1, 1);
|
||||
cursor_position--;
|
||||
on_change();
|
||||
return true;
|
||||
@@ -67,9 +102,9 @@ bool Input::OnEvent(Event event) {
|
||||
|
||||
// Delete
|
||||
if (event == Event::Delete) {
|
||||
if (cursor_position == int(content.size()))
|
||||
if (cursor_position == int(content_->size()))
|
||||
return false;
|
||||
content.erase(cursor_position, 1);
|
||||
content_->erase(cursor_position, 1);
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
@@ -89,7 +124,7 @@ bool Input::OnEvent(Event event) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == Event::ArrowRight && cursor_position < (int)content.size()) {
|
||||
if (event == Event::ArrowRight && cursor_position < (int)content_->size()) {
|
||||
cursor_position++;
|
||||
return true;
|
||||
}
|
||||
@@ -100,13 +135,13 @@ bool Input::OnEvent(Event event) {
|
||||
}
|
||||
|
||||
if (event == Event::End) {
|
||||
cursor_position = (int)content.size();
|
||||
cursor_position = (int)content_->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Content
|
||||
if (event.is_character()) {
|
||||
content.insert(cursor_position, 1, event.character());
|
||||
content_->insert(cursor_position, 1, event.character());
|
||||
cursor_position++;
|
||||
on_change();
|
||||
return true;
|
||||
@@ -114,7 +149,7 @@ bool Input::OnEvent(Event event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Input::OnMouseEvent(Event event) {
|
||||
bool InputBase::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
if (!input_box_.Contain(event.mouse().x, event.mouse().y))
|
||||
@@ -127,7 +162,7 @@ bool Input::OnMouseEvent(Event event) {
|
||||
int new_cursor_position =
|
||||
cursor_position + event.mouse().x - cursor_box_.x_min;
|
||||
new_cursor_position =
|
||||
std::max(0, std::min<int>(content.size(), new_cursor_position));
|
||||
std::max(0, std::min<int>(content_->size(), new_cursor_position));
|
||||
if (cursor_position != new_cursor_position) {
|
||||
cursor_position = new_cursor_position;
|
||||
on_change();
|
||||
|
@@ -1,159 +1,181 @@
|
||||
#include "ftxui/component/input.hpp"
|
||||
#include "ftxui/component/event.hpp"
|
||||
#include <gtest/gtest-message.h> // for Message
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult
|
||||
#include <memory> // for __shared_ptr_access
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ftxui/component/captured_mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Delete, Event::End, Event::Home
|
||||
#include "ftxui/component/input.hpp"
|
||||
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, SuiteApiResolver, TEST, TestFactoryImpl
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
TEST(InputTest, Init) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
EXPECT_EQ(input.content, L"");
|
||||
EXPECT_EQ(input.placeholder, L"");
|
||||
EXPECT_EQ(input.cursor_position, 0);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0);
|
||||
}
|
||||
|
||||
TEST(InputTest, Type) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
EXPECT_EQ(input.content, L"a");
|
||||
EXPECT_EQ(input.cursor_position, 1u);
|
||||
input->OnEvent(Event::Character('a'));
|
||||
EXPECT_EQ(content, L"a");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
|
||||
|
||||
input.OnEvent(Event::Character('b'));
|
||||
EXPECT_EQ(input.content, L"ab");
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
input->OnEvent(Event::Character('b'));
|
||||
EXPECT_EQ(content, L"ab");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
}
|
||||
|
||||
TEST(InputTest, Arrow) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
|
||||
EXPECT_EQ(input.cursor_position, 3u);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(input.cursor_position, 1u);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(input.cursor_position, 0u);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(input.cursor_position, 0u);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
|
||||
|
||||
input.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(input.cursor_position, 1u);
|
||||
input->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
|
||||
|
||||
input.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
input->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
|
||||
input.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(input.cursor_position, 3u);
|
||||
input->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
|
||||
|
||||
input.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(input.cursor_position, 3u);
|
||||
input->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
|
||||
}
|
||||
|
||||
TEST(InputTest, Insert) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
EXPECT_EQ(input.content, L"abc");
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
EXPECT_EQ(content, L"abc");
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(input.content, L"a-bc");
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(content, L"a-bc");
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(input.content, L"a--bc");
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(content, L"a--bc");
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(input.content, L"-a--bc");
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(content, L"-a--bc");
|
||||
}
|
||||
|
||||
TEST(InputTest, Home) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
EXPECT_EQ(input.content, L"abc");
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
EXPECT_EQ(content, L"abc");
|
||||
|
||||
EXPECT_EQ(input.cursor_position, 3u);
|
||||
input.OnEvent(Event::Home);
|
||||
EXPECT_EQ(input.cursor_position, 0u);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
|
||||
input->OnEvent(Event::Home);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
|
||||
|
||||
input.OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(input.content, L"-abc");
|
||||
input->OnEvent(Event::Character('-'));
|
||||
EXPECT_EQ(content, L"-abc");
|
||||
}
|
||||
|
||||
TEST(InputTest, End) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
|
||||
EXPECT_EQ(input.cursor_position, 1u);
|
||||
input.OnEvent(Event::End);
|
||||
EXPECT_EQ(input.cursor_position, 3u);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
|
||||
input->OnEvent(Event::End);
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 3u);
|
||||
}
|
||||
|
||||
TEST(InputTest, Delete) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
|
||||
EXPECT_EQ(input.content, L"abc");
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
EXPECT_EQ(content, L"abc");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
|
||||
input.OnEvent(Event::Delete);
|
||||
EXPECT_EQ(input.content, L"ab");
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
input->OnEvent(Event::Delete);
|
||||
EXPECT_EQ(content, L"ab");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
|
||||
input.OnEvent(Event::Delete);
|
||||
EXPECT_EQ(input.content, L"ab");
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
input->OnEvent(Event::Delete);
|
||||
EXPECT_EQ(content, L"ab");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
}
|
||||
|
||||
TEST(InputTest, Backspace) {
|
||||
Input input;
|
||||
std::wstring content;
|
||||
std::wstring placeholder;
|
||||
Component input = Input(&content, &placeholder);
|
||||
|
||||
input.OnEvent(Event::Character('a'));
|
||||
input.OnEvent(Event::Character('b'));
|
||||
input.OnEvent(Event::Character('c'));
|
||||
input.OnEvent(Event::ArrowLeft);
|
||||
input->OnEvent(Event::Character('a'));
|
||||
input->OnEvent(Event::Character('b'));
|
||||
input->OnEvent(Event::Character('c'));
|
||||
input->OnEvent(Event::ArrowLeft);
|
||||
|
||||
EXPECT_EQ(input.content, L"abc");
|
||||
EXPECT_EQ(input.cursor_position, 2u);
|
||||
EXPECT_EQ(content, L"abc");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 2u);
|
||||
|
||||
input.OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(input.content, L"ac");
|
||||
EXPECT_EQ(input.cursor_position, 1u);
|
||||
input->OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(content, L"ac");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 1u);
|
||||
|
||||
input.OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(input.content, L"c");
|
||||
EXPECT_EQ(input.cursor_position, 0u);
|
||||
input->OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(content, L"c");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
|
||||
|
||||
input.OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(input.content, L"c");
|
||||
EXPECT_EQ(input.cursor_position, 0u);
|
||||
input->OnEvent(Event::Backspace);
|
||||
EXPECT_EQ(content, L"c");
|
||||
EXPECT_EQ(InputBase::From(input)->cursor_position, 0u);
|
||||
}
|
||||
|
||||
// Copyright 2021 Arthur Sonzogni. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be found in
|
||||
// the LICENSE file.
|
||||
|
@@ -1,23 +1,63 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <ext/alloc_traits.h> // for __alloc_traits<>::value_type
|
||||
#include <memory> // for shared_ptr, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/menu.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp"
|
||||
#include "ftxui/component/mouse.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element Menu::Render() {
|
||||
/// @brief A list of text. The focused element is selected.
|
||||
/// @param entries The list of entries in the menu.
|
||||
/// @param selected The index of the currently selected element.
|
||||
/// @ingroup component
|
||||
/// @see MenuBase
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::TerminalOutput();
|
||||
/// std::vector<std::wstring> entries = {
|
||||
/// L"entry 1",
|
||||
/// L"entry 2",
|
||||
/// L"entry 3",
|
||||
/// };
|
||||
/// int selected = 0;
|
||||
/// auto menu = Menu(&entries, &selected);
|
||||
/// screen.Loop(menu);
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// > entry 1
|
||||
/// entry 2
|
||||
/// entry 3
|
||||
/// ```
|
||||
Component Menu(const std::vector<std::wstring>* entries, int* selected) {
|
||||
return Make<MenuBase>(entries, selected);
|
||||
}
|
||||
|
||||
// static
|
||||
MenuBase* MenuBase::From(Component component) {
|
||||
return static_cast<MenuBase*>(component.get());
|
||||
}
|
||||
|
||||
MenuBase::MenuBase(const std::vector<std::wstring>* entries, int* selected)
|
||||
: entries_(entries), selected_(selected) {}
|
||||
|
||||
Element MenuBase::Render() {
|
||||
Elements elements;
|
||||
bool is_menu_focused = Focused();
|
||||
boxes_.resize(entries.size());
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
boxes_.resize(entries_->size());
|
||||
for (size_t i = 0; i < entries_->size(); ++i) {
|
||||
bool is_focused = (focused == int(i)) && is_menu_focused;
|
||||
bool is_selected = (selected == int(i));
|
||||
bool is_selected = (*selected_ == int(i));
|
||||
|
||||
auto style = is_selected
|
||||
? (is_focused ? selected_focused_style : selected_style)
|
||||
@@ -26,13 +66,13 @@ Element Menu::Render() {
|
||||
: is_menu_focused ? focus
|
||||
: select;
|
||||
auto icon = is_selected ? L"> " : L" ";
|
||||
elements.push_back(text(icon + entries[i]) | style | focus_management |
|
||||
elements.push_back(text(icon + entries_->at(i)) | style | focus_management |
|
||||
reflect(boxes_[i]));
|
||||
}
|
||||
return vbox(std::move(elements));
|
||||
}
|
||||
|
||||
bool Menu::OnEvent(Event event) {
|
||||
bool MenuBase::OnEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
if (event.is_mouse())
|
||||
@@ -41,20 +81,20 @@ bool Menu::OnEvent(Event event) {
|
||||
if (!Focused())
|
||||
return false;
|
||||
|
||||
int old_selected = selected;
|
||||
int old_selected = *selected_;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
selected--;
|
||||
(*selected_)--;
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
selected++;
|
||||
if (event == Event::Tab && entries.size())
|
||||
selected = (selected + 1) % entries.size();
|
||||
if (event == Event::TabReverse && entries.size())
|
||||
selected = (selected + entries.size() - 1) % entries.size();
|
||||
(*selected_)++;
|
||||
if (event == Event::Tab && entries_->size())
|
||||
*selected_ = (*selected_ + 1) % entries_->size();
|
||||
if (event == Event::TabReverse && entries_->size())
|
||||
*selected_ = (*selected_ + entries_->size() - 1) % entries_->size();
|
||||
|
||||
selected = std::max(0, std::min(int(entries.size()) - 1, selected));
|
||||
*selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_));
|
||||
|
||||
if (selected != old_selected) {
|
||||
focused = selected;
|
||||
if (*selected_ != old_selected) {
|
||||
focused = *selected_;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
@@ -67,7 +107,7 @@ bool Menu::OnEvent(Event event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Menu::OnMouseEvent(Event event) {
|
||||
bool MenuBase::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
for (int i = 0; i < boxes_.size(); ++i) {
|
||||
@@ -78,8 +118,8 @@ bool Menu::OnMouseEvent(Event event) {
|
||||
focused = i;
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Released) {
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
if (*selected_ != i) {
|
||||
*selected_ = i;
|
||||
on_change();
|
||||
}
|
||||
return true;
|
||||
|
@@ -1,36 +1,76 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <functional> // for function
|
||||
#include <memory> // for shared_ptr, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
|
||||
#include "ftxui/component/radiobox.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp"
|
||||
#include "ftxui/component/mouse.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element RadioBox::Render() {
|
||||
/// @brief A list of element, where only one can be selected.
|
||||
/// @param entries The list of entries in the list.
|
||||
/// @param selected The index of the currently selected element.
|
||||
/// @ingroup component
|
||||
/// @see RadioboxBase
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::TerminalOutput();
|
||||
/// std::vector<std::wstring> entries = {
|
||||
/// L"entry 1",
|
||||
/// L"entry 2",
|
||||
/// L"entry 3",
|
||||
/// };
|
||||
/// int selected = 0;
|
||||
/// auto menu = Radiobox(&entries, &selected);
|
||||
/// screen.Loop(menu);
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// ◉ entry 1
|
||||
/// ○ entry 2
|
||||
/// ○ entry 3
|
||||
/// ```
|
||||
Component Radiobox(const std::vector<std::wstring>* entries, int* selected) {
|
||||
return Make<RadioboxBase>(entries, selected);
|
||||
}
|
||||
|
||||
// static
|
||||
RadioboxBase* RadioboxBase::From(Component component) {
|
||||
return static_cast<RadioboxBase*>(component.get());
|
||||
}
|
||||
|
||||
RadioboxBase::RadioboxBase(const std::vector<std::wstring>* entries,
|
||||
int* selected_)
|
||||
: entries_(entries), selected_(selected_) {}
|
||||
|
||||
Element RadioboxBase::Render() {
|
||||
std::vector<Element> elements;
|
||||
bool is_focused = Focused();
|
||||
boxes_.resize(entries.size());
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
boxes_.resize(entries_->size());
|
||||
for (size_t i = 0; i < entries_->size(); ++i) {
|
||||
auto style =
|
||||
(focused == int(i) && is_focused) ? focused_style : unfocused_style;
|
||||
auto focus_management = (focused != int(i)) ? nothing
|
||||
: is_focused ? focus
|
||||
: select;
|
||||
|
||||
const std::wstring& symbol = selected == int(i) ? checked : unchecked;
|
||||
elements.push_back(hbox(text(symbol), text(entries[i]) | style) |
|
||||
const std::wstring& symbol = *selected_ == int(i) ? checked : unchecked;
|
||||
elements.push_back(hbox(text(symbol), text(entries_->at(i)) | style) |
|
||||
focus_management | reflect(boxes_[i]));
|
||||
}
|
||||
return vbox(std::move(elements));
|
||||
}
|
||||
|
||||
bool RadioBox::OnEvent(Event event) {
|
||||
bool RadioboxBase::OnEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
if (event.is_mouse())
|
||||
@@ -44,12 +84,12 @@ bool RadioBox::OnEvent(Event event) {
|
||||
new_focused--;
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
new_focused++;
|
||||
if (event == Event::Tab && entries.size())
|
||||
new_focused = (new_focused + 1) % entries.size();
|
||||
if (event == Event::TabReverse && entries.size())
|
||||
new_focused = (new_focused + entries.size() - 1) % entries.size();
|
||||
if (event == Event::Tab && entries_->size())
|
||||
new_focused = (new_focused + 1) % entries_->size();
|
||||
if (event == Event::TabReverse && entries_->size())
|
||||
new_focused = (new_focused + entries_->size() - 1) % entries_->size();
|
||||
|
||||
new_focused = std::max(0, std::min(int(entries.size()) - 1, new_focused));
|
||||
new_focused = std::max(0, std::min(int(entries_->size()) - 1, new_focused));
|
||||
|
||||
if (focused != new_focused) {
|
||||
focused = new_focused;
|
||||
@@ -57,14 +97,14 @@ bool RadioBox::OnEvent(Event event) {
|
||||
}
|
||||
|
||||
if (event == Event::Character(' ') || event == Event::Return) {
|
||||
selected = focused;
|
||||
*selected_ = focused;
|
||||
on_change();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RadioBox::OnMouseEvent(Event event) {
|
||||
bool RadioboxBase::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
for (int i = 0; i < boxes_.size(); ++i) {
|
||||
@@ -78,8 +118,8 @@ bool RadioBox::OnMouseEvent(Event event) {
|
||||
event.mouse().motion == Mouse::Pressed) {
|
||||
cursor_position = i;
|
||||
TakeFocus();
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
if (*selected_ != i) {
|
||||
*selected_ = i;
|
||||
on_change();
|
||||
}
|
||||
return true;
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#include <gtest/gtest-message.h> // for Message
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr
|
||||
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for ftxui
|
||||
#include "ftxui/component/radiobox.hpp"
|
||||
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
|
||||
@@ -9,76 +10,105 @@
|
||||
using namespace ftxui;
|
||||
|
||||
TEST(RadioboxTest, Navigation) {
|
||||
RadioBox radiobox;
|
||||
radiobox.entries = {L"1", L"2", L"3"};
|
||||
int selected = 0;
|
||||
std::vector<std::wstring> entries = {L"1", L"2", L"3"};
|
||||
auto radiobox = Radiobox(&entries, &selected);
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
|
||||
// With vim like characters.
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::Character('j'));
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::Character('k'));
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::Character('j'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::Character('j'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::Character('j'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::Character('k'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::Character('k'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::Character('k'));
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
|
||||
// With more entries
|
||||
radiobox.entries = {L"1", L"2", L"3"};
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::ArrowDown);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::ArrowUp);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
entries = {L"1", L"2", L"3"};
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::ArrowDown);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::ArrowUp);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
|
||||
// With tab.
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(radiobox.focused, 0);
|
||||
radiobox.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(radiobox.focused, 2);
|
||||
radiobox.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(radiobox.focused, 1);
|
||||
radiobox.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::Tab);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::Tab);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::Tab);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::Tab);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::Tab);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::TabReverse);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::TabReverse);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 0);
|
||||
radiobox->OnEvent(Event::TabReverse);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 2);
|
||||
radiobox->OnEvent(Event::TabReverse);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
EXPECT_EQ(selected, 1);
|
||||
radiobox->OnEvent(Event::TabReverse);
|
||||
radiobox->OnEvent(Event::Return);
|
||||
}
|
||||
|
||||
// Copyright 2020 Arthur Sonzogni. All rights reserved.
|
||||
|
@@ -1,24 +1,23 @@
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
|
||||
#include <stdio.h> // for fileno, stdin
|
||||
#include <algorithm> // for copy, max, min
|
||||
#include <csignal> // for signal, SIGINT
|
||||
#include <csignal> // for signal, SIGINT, SIGWINCH
|
||||
#include <cstdlib> // for exit, NULL
|
||||
#include <iostream> // for cout, ostream
|
||||
#include <stack> // for stack
|
||||
#include <thread> // for thread
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
|
||||
#include <stack> // for stack
|
||||
#include <thread> // for thread
|
||||
#include <utility> // for move
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Component
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/component/receiver.hpp" // for ReceiverImpl
|
||||
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputPa...
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/event.hpp" // for Event
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse
|
||||
#include "ftxui/component/receiver.hpp" // for ReceiverImpl, SenderImpl, MakeReceiver
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
|
||||
#include "ftxui/dom/node.hpp" // for Node, Render
|
||||
#include "ftxui/dom/requirement.hpp" // for Requirement
|
||||
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimen...
|
||||
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimensions, Terminal
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DEFINE_CONSOLEV2_PROPERTIES
|
||||
@@ -31,9 +30,9 @@
|
||||
#error Must be compiled in UNICODE mode
|
||||
#endif
|
||||
#else
|
||||
#include <sys/select.h> // for select, FD_ISSET
|
||||
#include <termios.h> // for tcsetattr, tcge...
|
||||
#include <unistd.h> // for STDIN_FILENO, read
|
||||
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
|
||||
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
|
||||
#include <unistd.h> // for STDIN_FILENO, read
|
||||
#endif
|
||||
|
||||
// Quick exit is missing in standard CLang headers
|
||||
@@ -110,7 +109,7 @@ void EventListener(std::atomic<bool>* quit, Sender<Event> out) {
|
||||
|
||||
char c;
|
||||
while (!*quit) {
|
||||
while(read(STDIN_FILENO, &c, 1), c)
|
||||
while (read(STDIN_FILENO, &c, 1), c)
|
||||
parser.Add(c);
|
||||
|
||||
emscripten_sleep(1);
|
||||
@@ -278,7 +277,7 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
|
||||
[this] { mouse_captured = false; });
|
||||
}
|
||||
|
||||
void ScreenInteractive::Loop(Component* component) {
|
||||
void ScreenInteractive::Loop(Component component) {
|
||||
// Install a SIGINT handler and restore the old handler on exit.
|
||||
auto old_sigint_handler = std::signal(SIGINT, OnExit);
|
||||
on_exit_functions.push(
|
||||
@@ -417,7 +416,7 @@ void ScreenInteractive::Loop(Component* component) {
|
||||
OnExit(0);
|
||||
}
|
||||
|
||||
void ScreenInteractive::Draw(Component* component) {
|
||||
void ScreenInteractive::Draw(Component component) {
|
||||
auto document = component->Render();
|
||||
int dimx = 0;
|
||||
int dimy = 0;
|
||||
|
@@ -1,21 +1,22 @@
|
||||
#include "ftxui/component/slider.hpp"
|
||||
#include <string> // for allocator, wstring
|
||||
#include <utility> // for move
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp"
|
||||
#include "ftxui/component/mouse.hpp"
|
||||
#include "ftxui/component/screen_interactive.hpp"
|
||||
#include "ftxui/dom/elements.hpp"
|
||||
#include "ftxui/screen/box.hpp"
|
||||
#include "ftxui/screen/color.hpp"
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/component.hpp" // for Make, Slider
|
||||
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
||||
#include "ftxui/component/screen_interactive.hpp" // for Component
|
||||
#include "ftxui/dom/elements.hpp" // for Element, text, color, operator|, xflex, gauge, dim, hbox, reflect, underlined, vcenter
|
||||
#include "ftxui/screen/box.hpp" // for Box
|
||||
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::GrayLight
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
template <class T>
|
||||
class SliderImpl : public Component {
|
||||
class SliderBase : public ComponentBase {
|
||||
public:
|
||||
SliderImpl(std::wstring label, T* value, T min, T max, T increment)
|
||||
SliderBase(std::wstring label, T* value, T min, T max, T increment)
|
||||
: label_(label),
|
||||
value_(value),
|
||||
min_(min),
|
||||
@@ -53,7 +54,7 @@ class SliderImpl : public Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Component::OnEvent(event);
|
||||
return ComponentBase::OnEvent(event);
|
||||
}
|
||||
|
||||
bool OnMouseEvent(Event event) {
|
||||
@@ -62,8 +63,7 @@ class SliderImpl : public Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (box_.Contain(event.mouse().x, event.mouse().y) &&
|
||||
CaptureMouse(event)) {
|
||||
if (box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event)) {
|
||||
TakeFocus();
|
||||
}
|
||||
|
||||
@@ -94,23 +94,44 @@ class SliderImpl : public Component {
|
||||
CapturedMouse captured_mouse_;
|
||||
};
|
||||
|
||||
/// @brief An horizontal slider.
|
||||
/// @param label The name of the slider.
|
||||
/// @param value The current value of the slider.
|
||||
/// @param min The minimum value.
|
||||
/// @param max The maximum value.
|
||||
/// @param increment The increment when used by the cursor.
|
||||
/// @ingroup component
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```cpp
|
||||
/// auto screen = ScreenInteractive::TerminalOutput();
|
||||
/// int value = 50;
|
||||
/// auto slider = Slider(L"Value:", &value, 0, 100, 1);
|
||||
/// screen.Loop(slider);
|
||||
/// ```
|
||||
///
|
||||
/// ### Output
|
||||
///
|
||||
/// ```bash
|
||||
/// Value:[██████████████████████████ ]
|
||||
/// ```
|
||||
template <class T>
|
||||
ComponentPtr Slider(std::wstring label, T* value, T min, T max, T increment) {
|
||||
return std::make_unique<SliderImpl<T>>(std::move(label), value, min, max,
|
||||
increment);
|
||||
Component Slider(std::wstring label, T* value, T min, T max, T increment) {
|
||||
return Make<SliderBase<T>>(std::move(label), value, min, max, increment);
|
||||
}
|
||||
|
||||
template ComponentPtr Slider(std::wstring label,
|
||||
int* value,
|
||||
int min,
|
||||
int max,
|
||||
int increment);
|
||||
template Component Slider(std::wstring label,
|
||||
int* value,
|
||||
int min,
|
||||
int max,
|
||||
int increment);
|
||||
|
||||
template ComponentPtr Slider(std::wstring label,
|
||||
float* value,
|
||||
float min,
|
||||
float max,
|
||||
float increment);
|
||||
template Component Slider(std::wstring label,
|
||||
float* value,
|
||||
float min,
|
||||
float max,
|
||||
float increment);
|
||||
|
||||
} // namespace ftxui
|
||||
|
||||
|
@@ -1,26 +1,38 @@
|
||||
#include <stddef.h> // for size_t
|
||||
#include <algorithm> // for max, min
|
||||
#include <memory> // for shared_ptr, alloca...
|
||||
#include <memory> // for shared_ptr, allocator_traits<>::value_type
|
||||
#include <utility> // for move
|
||||
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left
|
||||
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
|
||||
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
|
||||
#include "ftxui/component/toggle.hpp"
|
||||
|
||||
namespace ftxui {
|
||||
|
||||
Element Toggle::Render() {
|
||||
Component Toggle(const std::vector<std::wstring>* entries, int* selected) {
|
||||
return Make<ToggleBase>(entries, selected);
|
||||
}
|
||||
|
||||
// static
|
||||
ToggleBase* ToggleBase::From(Component component) {
|
||||
return static_cast<ToggleBase*>(component.get());
|
||||
}
|
||||
|
||||
ToggleBase::ToggleBase(const std::vector<std::wstring>* entries, int* selected)
|
||||
: entries_(entries), selected_(selected) {}
|
||||
|
||||
Element ToggleBase::Render() {
|
||||
Elements children;
|
||||
bool is_toggle_focused = Focused();
|
||||
boxes_.resize(entries.size());
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
boxes_.resize(entries_->size());
|
||||
for (size_t i = 0; i < entries_->size(); ++i) {
|
||||
// Separator.
|
||||
if (i != 0)
|
||||
children.push_back(separator());
|
||||
|
||||
bool is_focused = (focused == int(i)) && is_toggle_focused;
|
||||
bool is_selected = (selected == int(i));
|
||||
bool is_selected = (*selected_ == int(i));
|
||||
|
||||
auto style = is_selected
|
||||
? (is_focused ? selected_focused_style : selected_style)
|
||||
@@ -28,30 +40,30 @@ Element Toggle::Render() {
|
||||
auto focus_management = !is_selected ? nothing
|
||||
: is_toggle_focused ? focus
|
||||
: select;
|
||||
children.push_back(text(entries[i]) | style | focus_management |
|
||||
children.push_back(text(entries_->at(i)) | style | focus_management |
|
||||
reflect(boxes_[i]));
|
||||
}
|
||||
return hbox(std::move(children));
|
||||
}
|
||||
|
||||
bool Toggle::OnEvent(Event event) {
|
||||
bool ToggleBase::OnEvent(Event event) {
|
||||
if (event.is_mouse())
|
||||
return OnMouseEvent(event);
|
||||
|
||||
int old_selected = selected;
|
||||
int old_selected = *selected_;
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h'))
|
||||
selected--;
|
||||
(*selected_)--;
|
||||
if (event == Event::ArrowRight || event == Event::Character('l'))
|
||||
selected++;
|
||||
if (event == Event::Tab && entries.size())
|
||||
selected = (selected + 1) % entries.size();
|
||||
if (event == Event::TabReverse && entries.size())
|
||||
selected = (selected + entries.size() - 1) % entries.size();
|
||||
(*selected_)++;
|
||||
if (event == Event::Tab && entries_->size())
|
||||
*selected_ = (*selected_ + 1) % entries_->size();
|
||||
if (event == Event::TabReverse && entries_->size())
|
||||
*selected_ = (*selected_ + entries_->size() - 1) % entries_->size();
|
||||
|
||||
selected = std::max(0, std::min(int(entries.size()) - 1, selected));
|
||||
*selected_ = std::max(0, std::min(int(entries_->size()) - 1, *selected_));
|
||||
|
||||
if (old_selected != selected) {
|
||||
focused = selected;
|
||||
if (old_selected != *selected_) {
|
||||
focused = *selected_;
|
||||
on_change();
|
||||
return true;
|
||||
}
|
||||
@@ -64,7 +76,7 @@ bool Toggle::OnEvent(Event event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Toggle::OnMouseEvent(Event event) {
|
||||
bool ToggleBase::OnMouseEvent(Event event) {
|
||||
if (!CaptureMouse(event))
|
||||
return false;
|
||||
for (int i = 0; i < boxes_.size(); ++i) {
|
||||
@@ -76,8 +88,8 @@ bool Toggle::OnMouseEvent(Event event) {
|
||||
if (event.mouse().button == Mouse::Left &&
|
||||
event.mouse().motion == Mouse::Pressed) {
|
||||
TakeFocus();
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
if (*selected_ != i) {
|
||||
*selected_ = i;
|
||||
on_change();
|
||||
}
|
||||
return true;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include <gtest/gtest-message.h> // for Message
|
||||
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
|
||||
#include <memory> // for __shared_ptr_access, shared_ptr
|
||||
|
||||
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
|
||||
#include "ftxui/component/mouse.hpp" // for ftxui
|
||||
@@ -9,131 +10,136 @@
|
||||
using namespace ftxui;
|
||||
|
||||
TEST(ToggleTest, leftRightArrow) {
|
||||
Toggle toggle;
|
||||
std::vector<std::wstring> entries = {L"On", L"Off"};
|
||||
int selected = 0;
|
||||
auto toggle = Toggle(&entries, &selected);
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(selected, 0);
|
||||
|
||||
// With vim like characters.
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::Character('l'));
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::Character('h'));
|
||||
EXPECT_EQ(selected, 0);
|
||||
|
||||
// With more entries
|
||||
toggle.entries = {L"1", L"2", L"3"};
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(toggle.selected, 2);
|
||||
toggle.OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(toggle.selected, 2);
|
||||
toggle.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
entries = {L"1", L"2", L"3"};
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(selected, 2);
|
||||
toggle->OnEvent(Event::ArrowRight);
|
||||
EXPECT_EQ(selected, 2);
|
||||
toggle->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::ArrowLeft);
|
||||
EXPECT_EQ(selected, 0);
|
||||
}
|
||||
|
||||
TEST(ToggleTest, Tab) {
|
||||
Toggle toggle;
|
||||
toggle.entries = {L"1", L"2", L"3"};
|
||||
std::vector<std::wstring> entries = {L"1", L"2", L"3"};
|
||||
int selected = 0;
|
||||
auto toggle = Toggle(&entries, &selected);
|
||||
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(toggle.selected, 2);
|
||||
toggle.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::Tab);
|
||||
EXPECT_EQ(toggle.selected, 2);
|
||||
toggle.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(toggle.selected, 0);
|
||||
toggle.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(toggle.selected, 2);
|
||||
toggle.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(toggle.selected, 1);
|
||||
toggle.OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(selected, 2);
|
||||
toggle->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::Tab);
|
||||
EXPECT_EQ(selected, 2);
|
||||
toggle->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 0);
|
||||
toggle->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 2);
|
||||
toggle->OnEvent(Event::TabReverse);
|
||||
EXPECT_EQ(selected, 1);
|
||||
toggle->OnEvent(Event::TabReverse);
|
||||
}
|
||||
|
||||
TEST(ToggleTest, OnChange) {
|
||||
Toggle toggle;
|
||||
toggle.entries = {L"1", L"2", L"3"};
|
||||
std::vector<std::wstring> entries = {L"1", L"2", L"3"};
|
||||
int selected = 0;
|
||||
auto toggle = Toggle(&entries, &selected);
|
||||
|
||||
int counter = 0;
|
||||
toggle.on_change = [&] { counter++; };
|
||||
ToggleBase::From(toggle)->on_change = [&] { counter++; };
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_EQ(counter, 0);
|
||||
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [0] -> [1]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [0] -> [1]
|
||||
EXPECT_EQ(counter, 1);
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [1] -> [2]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [1] -> [2]
|
||||
EXPECT_EQ(counter, 2);
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowRight)); // Reached far right.
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowRight)); // Reached far right.
|
||||
EXPECT_EQ(counter, 2);
|
||||
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [2] -> [1]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [2] -> [1]
|
||||
EXPECT_EQ(counter, 3);
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [1] -> [0]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [1] -> [0]
|
||||
EXPECT_EQ(counter, 4);
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_EQ(counter, 4);
|
||||
}
|
||||
|
||||
TEST(ToggleTest, OnEnter) {
|
||||
Toggle toggle;
|
||||
toggle.entries = {L"1", L"2", L"3"};
|
||||
std::vector<std::wstring> entries = {L"1", L"2", L"3"};
|
||||
int selected = 0;
|
||||
auto toggle = Toggle(&entries, &selected);
|
||||
|
||||
int counter = 0;
|
||||
toggle.on_enter = [&] { counter++; };
|
||||
ToggleBase::From(toggle)->on_enter = [&] { counter++; };
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 1);
|
||||
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [0] -> [1]
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [0] -> [1]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 2);
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowRight)); // [1] -> [2]
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowRight)); // [1] -> [2]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 3);
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowRight)); // Reached far right.
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowRight)); // Reached far right.
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 4);
|
||||
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [2] -> [1]
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [2] -> [1]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 5);
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::ArrowLeft)); // [1] -> [0]
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::ArrowLeft)); // [1] -> [0]
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 6);
|
||||
|
||||
EXPECT_FALSE(toggle.OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_TRUE(toggle.OnEvent(Event::Return));
|
||||
EXPECT_FALSE(toggle->OnEvent(Event::ArrowLeft)); // Reached far left.
|
||||
EXPECT_TRUE(toggle->OnEvent(Event::Return));
|
||||
EXPECT_EQ(counter, 7);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user