Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge dom and component focus #978

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ current (development)
- See `selectionForegroundColor` decorator.
- See `selectionStyle(style)` decorator.
- See `selectionStyleReset` decorator.
- Breaking change: Change how "focus"/"select" are handled. This fixes the
behavior.

### Screen
- Feature: Add `Box::IsEmpty()`.
Expand Down
7 changes: 6 additions & 1 deletion include/ftxui/dom/elements.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,13 @@ Decorator selectionStyle(std::function<void(Pixel&)> style);

// --- Layout is
// Horizontal, Vertical or stacked set of elements.
// The extra `int` argument is used to select the `focus` element among the
// children. The `focus` element is the one that will be visible when the
// element is inside a `frame`.
Element hbox(Elements);
Element hbox(Elements, int);
Element vbox(Elements);
Element vbox(Elements, int);
Element dbox(Elements);
Element flexbox(Elements, FlexboxConfig config = FlexboxConfig());
Element gridbox(std::vector<Elements> lines);
Expand Down Expand Up @@ -161,7 +166,7 @@ Element frame(Element);
Element xframe(Element);
Element yframe(Element);
Element focus(Element);
Element select(Element);
Element select(Element e); // Deprecated - Alias for focus.

// --- Cursor ---
// Those are similar to `focus`, but also change the shape of the cursor.
Expand Down
11 changes: 4 additions & 7 deletions include/ftxui/dom/requirement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define FTXUI_DOM_REQUIREMENT_HPP

#include "ftxui/screen/box.hpp"
#include "ftxui/screen/screen.hpp"

namespace ftxui {

Expand All @@ -20,13 +21,9 @@ struct Requirement {
int flex_shrink_y = 0;

// Focus management to support the frame/focus/select element.
enum Selection {
NORMAL = 0,
SELECTED = 1,
FOCUSED = 2,
};
Selection selection = NORMAL;
Box selected_box;
bool is_focused = false;
Box focused_box;
Screen::Cursor::Shape cursor_shape = Screen::Cursor::Shape::Hidden;
};

} // namespace ftxui
Expand Down
6 changes: 4 additions & 2 deletions src/ftxui/component/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,16 @@ class ButtonBase : public ComponentBase, public ButtonOption {
SetAnimationTarget(target);
}

auto focus_management = focused ? focus : active ? select : nothing;
const EntryState state{
*label, false, active, focused_or_hover, Index(),
};

auto element = (transform ? transform : DefaultTransform) //
(state);
return element | AnimatedColorStyle() | focus_management | reflect(box_);
element |= AnimatedColorStyle();
element |= focus;
element |= reflect(box_);
return element;
}

Decorator AnimatedColorStyle() {
Expand Down
5 changes: 3 additions & 2 deletions src/ftxui/component/checkbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
Element Render() override {
const bool is_focused = Focused();
const bool is_active = Active();
auto focus_management = is_focused ? focus : is_active ? select : nothing;
auto entry_state = EntryState{
*label, *checked, is_active, is_focused || hovered_, -1,
};
auto element = (transform ? transform : CheckboxOption::Simple().transform)(
entry_state);
return element | focus_management | reflect(box_);
element |= focus;
element |= reflect(box_);
return element;
}

bool OnEvent(Event event) override {
Expand Down
8 changes: 4 additions & 4 deletions src/ftxui/component/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class VerticalContainer : public ContainerBase {
if (elements.empty()) {
return text("Empty container") | reflect(box_);
}
return vbox(std::move(elements)) | reflect(box_);
return vbox(std::move(elements), *selector_) | reflect(box_);
}

bool EventHandler(Event event) override {
Expand Down Expand Up @@ -191,7 +191,7 @@ class HorizontalContainer : public ContainerBase {
if (elements.empty()) {
return text("Empty container");
}
return hbox(std::move(elements));
return hbox(std::move(elements), *selector_);
}

bool EventHandler(Event event) override {
Expand Down Expand Up @@ -334,7 +334,7 @@ Component Vertical(Components children) {
/// children_2,
/// children_3,
/// children_4,
/// });
/// }, &selected_children);
/// ```
Component Vertical(Components children, int* selector) {
return std::make_shared<VerticalContainer>(std::move(children), selector);
Expand All @@ -355,7 +355,7 @@ Component Vertical(Components children, int* selector) {
/// children_2,
/// children_3,
/// children_4,
/// }, &selected_children);
/// });
/// ```
Component Horizontal(Components children) {
return Horizontal(std::move(children), nullptr);
Expand Down
5 changes: 1 addition & 4 deletions src/ftxui/component/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class InputBase : public ComponentBase, public InputOption {
// Component implementation:
Element Render() override {
const bool is_focused = Focused();
const auto focused = (!is_focused && !hovered_) ? select
const auto focused = (!is_focused && !hovered_) ? focus
: insert() ? focusCursorBarBlinking
: focusCursorBlockBlinking;

Expand All @@ -108,9 +108,6 @@ class InputBase : public ComponentBase, public InputOption {
// placeholder.
if (content->empty()) {
auto element = text(placeholder()) | xflex | frame;
if (is_focused) {
element |= focus;
}

return transform_func({
std::move(element), hovered_, is_focused,
Expand Down
51 changes: 26 additions & 25 deletions src/ftxui/component/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,13 @@ class MenuBase : public ComponentBase, public MenuOption {
entries[i], false, is_selected, is_focused, i,
};

auto focus_management = (selected_focus_ != i) ? nothing
: is_menu_focused ? focus
: select;

const Element element =
(entries_option.transform ? entries_option.transform
: DefaultOptionTransform) //
Element element = (entries_option.transform ? entries_option.transform
: DefaultOptionTransform) //
(state);
elements.push_back(element | AnimatedColorStyle(i) | reflect(boxes_[i]) |
focus_management);
element |= focus;
element |= AnimatedColorStyle(i);
element |= reflect(boxes_[i]);
elements.push_back(element);
}
if (elements_postfix) {
elements.push_back(elements_postfix());
Expand All @@ -145,28 +142,33 @@ class MenuBase : public ComponentBase, public MenuOption {
std::reverse(elements.begin(), elements.end());
}

const Element bar =
IsHorizontal() ? hbox(std::move(elements)) : vbox(std::move(elements));
const Element bar = IsHorizontal()
? hbox(std::move(elements), selected_focus_)
: vbox(std::move(elements), selected_focus_);

if (!underline.enabled) {
return bar | reflect(box_);
}

if (IsHorizontal()) {
return vbox({
bar | xflex,
separatorHSelector(first_, second_, //
underline.color_active,
underline.color_inactive),
}) |
return vbox(
{
bar | xflex,
separatorHSelector(first_, second_, //
underline.color_active,
underline.color_inactive),
},
0) |
reflect(box_);
} else {
return hbox({
separatorVSelector(first_, second_, //
underline.color_active,
underline.color_inactive),
bar | yflex,
}) |
return hbox(
{
separatorVSelector(first_, second_, //
underline.color_active,
underline.color_inactive),
bar | yflex,
},
0) |
reflect(box_);
}
}
Expand Down Expand Up @@ -630,8 +632,7 @@ Component MenuEntry(MenuEntryOption option) {
(transform ? transform : DefaultOptionTransform) //
(state);

auto focus_management = focused ? select : nothing;
return element | AnimatedColorStyle() | focus_management | reflect(box_);
return element | AnimatedColorStyle() | reflect(box_);
}

void UpdateAnimationTarget() {
Expand Down
7 changes: 2 additions & 5 deletions src/ftxui/component/radiobox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,15 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
for (int i = 0; i < size(); ++i) {
const bool is_focused = (focused_entry() == i) && is_menu_focused;
const bool is_selected = (hovered_ == i);
auto focus_management = !is_selected ? nothing
: is_menu_focused ? focus
: select;
auto state = EntryState{
entries[i], selected() == i, is_selected, is_focused, i,
};
auto element =
(transform ? transform : RadioboxOption::Simple().transform)(state);

elements.push_back(element | focus_management | reflect(boxes_[i]));
elements.push_back(element | focus | reflect(boxes_[i]));
}
return vbox(std::move(elements)) | reflect(box_);
return vbox(std::move(elements), focused_entry()) | reflect(box_);
}

// NOLINTNEXTLINE(readability-function-cognitive-complexity)
Expand Down
90 changes: 44 additions & 46 deletions src/ftxui/component/slider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,53 +134,52 @@ class SliderBase : public SliderOption<T>, public ComponentBase {
return ComponentBase::OnEvent(event);
}

bool OnMouseEvent(Event event) {
if (captured_mouse_) {
if (event.mouse().motion == Mouse::Released) {
captured_mouse_ = nullptr;
return true;
}
bool OnCapturedMouseEvent(Event event) {
if (event.mouse().motion == Mouse::Released) {
captured_mouse_ = nullptr;
return true;
}

T old_value = this->value();
switch (this->direction) {
case Direction::Right: {
this->value() = this->min() + (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);

T old_value = this->value();
switch (this->direction) {
case Direction::Right: {
this->value() =
this->min() + (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);

break;
}
case Direction::Left: {
this->value() =
this->max() - (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break;
}
case Direction::Down: {
this->value() =
this->min() + (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
case Direction::Up: {
this->value() =
this->max() - (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
break;
}
case Direction::Left: {
this->value() = this->max() - (event.mouse().x - gauge_box_.x_min) *
(this->max() - this->min()) /
(gauge_box_.x_max - gauge_box_.x_min);
break;
}
case Direction::Down: {
this->value() = this->min() + (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
case Direction::Up: {
this->value() = this->max() - (event.mouse().y - gauge_box_.y_min) *
(this->max() - this->min()) /
(gauge_box_.y_max - gauge_box_.y_min);
break;
}
}

this->value() =
std::max(this->min(), std::min(this->max(), this->value()));
this->value() = std::max(this->min(), std::min(this->max(), this->value()));

if (old_value != this->value() && this->on_change) {
this->on_change();
}
return true;
if (old_value != this->value() && this->on_change) {
this->on_change();
}
return true;
}

bool OnMouseEvent(Event event) {
if (captured_mouse_) {
return OnCapturedMouseEvent(event);
}

if (event.mouse().button != Mouse::Left) {
Expand All @@ -198,7 +197,7 @@ class SliderBase : public SliderOption<T>, public ComponentBase {

if (captured_mouse_) {
TakeFocus();
return true;
return OnCapturedMouseEvent(event);
}

return false;
Expand Down Expand Up @@ -243,7 +242,6 @@ class SliderWithLabel : public ComponentBase {
}

Element Render() override {
auto focus_management = Focused() ? focus : Active() ? select : nothing;
auto gauge_color = (Focused() || mouse_hover_) ? color(Color::White)
: color(Color::GrayDark);
return hbox({
Expand All @@ -254,7 +252,7 @@ class SliderWithLabel : public ComponentBase {
text("]"),
}) | xflex,
}) |
gauge_color | xflex | reflect(box_) | focus_management;
gauge_color | xflex | reflect(box_) | focus;
}

ConstStringRef label_;
Expand Down
Loading