mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-22 10:40:00 +08:00
Add non focusable components. (#172)
This commit is contained in:
parent
49e8cc57d3
commit
26e26fd41a
@ -51,6 +51,11 @@ class ComponentBase {
|
||||
// root component contains this object.
|
||||
virtual Component ActiveChild();
|
||||
|
||||
// Return true when the component contains focusable elements.
|
||||
// The non focusable Component will be skipped when navigating using the
|
||||
// keyboard.
|
||||
virtual bool Focusable() const;
|
||||
|
||||
// Whether this is the active child of its parent.
|
||||
bool Active() const;
|
||||
// Whether all the ancestors are active.
|
||||
|
@ -53,6 +53,10 @@ class ButtonBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ConstStringRef label_;
|
||||
std::function<void()> on_click_;
|
||||
|
@ -71,6 +71,10 @@ class CheckboxBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return true;
|
||||
}
|
||||
|
||||
ConstStringRef label_;
|
||||
bool* const state_;
|
||||
Box box_;
|
||||
|
@ -106,6 +106,18 @@ Component ComponentBase::ActiveChild() {
|
||||
return children_.empty() ? nullptr : children_.front();
|
||||
}
|
||||
|
||||
/// @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 {
|
||||
for (const Component& child : children_) {
|
||||
if (child->Focusable())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief Returns if the element if the currently active child of its parent.
|
||||
/// @ingroup component
|
||||
bool ComponentBase::Active() const {
|
||||
|
@ -63,6 +63,25 @@ class ContainerBase : public ComponentBase {
|
||||
|
||||
int selected_ = 0;
|
||||
int* selector_ = nullptr;
|
||||
|
||||
void MoveSelector(int dir) {
|
||||
for (int i = *selector_ + dir; i >= 0 && i < (int)children_.size();
|
||||
i += dir) {
|
||||
if (children_[i]->Focusable()) {
|
||||
*selector_ = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void MoveSelectorWrap(int dir) {
|
||||
for (size_t offset = 1; offset < children_.size(); ++offset) {
|
||||
int i = (*selector_ + offset * dir + children_.size()) % children_.size();
|
||||
if (children_[i]->Focusable()) {
|
||||
*selector_ = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class VerticalContainer : public ContainerBase {
|
||||
@ -81,13 +100,13 @@ class VerticalContainer : public ContainerBase {
|
||||
bool EventHandler(Event event) override {
|
||||
int old_selected = *selector_;
|
||||
if (event == Event::ArrowUp || event == Event::Character('k'))
|
||||
(*selector_)--;
|
||||
MoveSelector(-1);
|
||||
if (event == Event::ArrowDown || event == Event::Character('j'))
|
||||
(*selector_)++;
|
||||
MoveSelector(+1);
|
||||
if (event == Event::Tab && children_.size())
|
||||
*selector_ = (*selector_ + 1) % children_.size();
|
||||
MoveSelectorWrap(+1);
|
||||
if (event == Event::TabReverse && children_.size())
|
||||
*selector_ = (*selector_ + children_.size() - 1) % children_.size();
|
||||
MoveSelectorWrap(-1);
|
||||
|
||||
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
|
||||
return old_selected != *selector_;
|
||||
@ -110,13 +129,13 @@ class HorizontalContainer : public ContainerBase {
|
||||
bool EventHandler(Event event) override {
|
||||
int old_selected = *selector_;
|
||||
if (event == Event::ArrowLeft || event == Event::Character('h'))
|
||||
(*selector_)--;
|
||||
MoveSelector(-1);
|
||||
if (event == Event::ArrowRight || event == Event::Character('l'))
|
||||
(*selector_)++;
|
||||
MoveSelector(+1);
|
||||
if (event == Event::Tab && children_.size())
|
||||
*selector_ = (*selector_ + 1) % children_.size();
|
||||
MoveSelectorWrap(+1);
|
||||
if (event == Event::TabReverse && children_.size())
|
||||
*selector_ = (*selector_ + children_.size() - 1) % children_.size();
|
||||
MoveSelectorWrap(-1);
|
||||
|
||||
*selector_ = std::max(0, std::min(int(children_.size()) - 1, *selector_));
|
||||
return old_selected != *selector_;
|
||||
|
@ -10,14 +10,24 @@
|
||||
|
||||
using namespace ftxui;
|
||||
|
||||
Component Focusable() {
|
||||
return Button(L"", [] {});
|
||||
}
|
||||
Component NonFocusable() {
|
||||
return Container::Horizontal({});
|
||||
}
|
||||
|
||||
TEST(ContainerTest, HorizontalEvent) {
|
||||
auto container = Container::Horizontal({});
|
||||
auto c0 = Container::Horizontal({});
|
||||
auto c1 = Container::Horizontal({});
|
||||
auto c2 = Container::Horizontal({});
|
||||
auto c0 = Focusable();
|
||||
auto c1 = Focusable();
|
||||
auto c2 = Focusable();
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(NonFocusable());
|
||||
container->Add(NonFocusable());
|
||||
container->Add(c2);
|
||||
container->Add(NonFocusable());
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
@ -85,12 +95,15 @@ TEST(ContainerTest, HorizontalEvent) {
|
||||
|
||||
TEST(ContainerTest, VerticalEvent) {
|
||||
auto container = Container::Vertical({});
|
||||
auto c0 = Container::Horizontal({});
|
||||
auto c1 = Container::Horizontal({});
|
||||
auto c2 = Container::Horizontal({});
|
||||
auto c0 = Focusable();
|
||||
auto c1 = Focusable();
|
||||
auto c2 = Focusable();
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(NonFocusable());
|
||||
container->Add(NonFocusable());
|
||||
container->Add(c2);
|
||||
container->Add(NonFocusable());
|
||||
|
||||
// With arrow key.
|
||||
EXPECT_EQ(container->ActiveChild(), c0);
|
||||
@ -158,9 +171,9 @@ TEST(ContainerTest, VerticalEvent) {
|
||||
|
||||
TEST(ContainerTest, SetActiveChild) {
|
||||
auto container = Container::Horizontal({});
|
||||
auto c0 = Container::Horizontal({});
|
||||
auto c1 = Container::Horizontal({});
|
||||
auto c2 = Container::Horizontal({});
|
||||
auto c0 = Focusable();
|
||||
auto c1 = Focusable();
|
||||
auto c2 = Focusable();
|
||||
container->Add(c0);
|
||||
container->Add(c1);
|
||||
container->Add(c2);
|
||||
@ -215,12 +228,12 @@ TEST(ContainerTest, TakeFocus) {
|
||||
auto c1 = Container::Vertical({});
|
||||
auto c2 = Container::Vertical({});
|
||||
auto c3 = Container::Vertical({});
|
||||
auto c11 = Container::Horizontal({});
|
||||
auto c12 = Container::Horizontal({});
|
||||
auto c13 = Container::Horizontal({});
|
||||
auto c21 = Container::Horizontal({});
|
||||
auto c22 = Container::Horizontal({});
|
||||
auto c23 = Container::Horizontal({});
|
||||
auto c11 = Focusable();
|
||||
auto c12 = Focusable();
|
||||
auto c13 = Focusable();
|
||||
auto c21 = Focusable();
|
||||
auto c22 = Focusable();
|
||||
auto c23 = Focusable();
|
||||
|
||||
c->Add(c1);
|
||||
c->Add(c2);
|
||||
|
@ -163,6 +163,11 @@ class InputBase : public ComponentBase {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return true;
|
||||
}
|
||||
|
||||
StringRef content_;
|
||||
ConstStringRef placeholder_;
|
||||
|
||||
|
@ -106,6 +106,10 @@ class MenuBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return entries_->size();
|
||||
}
|
||||
|
||||
int& focused_entry() { return option_->focused_entry(); }
|
||||
|
||||
protected:
|
||||
|
@ -119,6 +119,10 @@ class RadioboxBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return entries_->size();
|
||||
}
|
||||
|
||||
int& focused_entry() { return option_->focused_entry(); }
|
||||
|
||||
const std::vector<std::wstring>* const entries_;
|
||||
|
@ -84,6 +84,10 @@ class SliderBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef label_;
|
||||
T* value_;
|
||||
|
@ -107,6 +107,8 @@ class ToggleBase : public ComponentBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Focusable() const final { return entries_->size(); }
|
||||
|
||||
int& focused_entry() { return option_->focused_entry(); }
|
||||
|
||||
const std::vector<std::wstring>* const entries_;
|
||||
|
Loading…
Reference in New Issue
Block a user