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