Compare commits

...

3 Commits

Author SHA1 Message Date
Alex
abf1ae9cb5
Merge baa5973128 into 1d40687a40 2024-10-01 23:26:14 +03:00
Herring
1d40687a40
Add index to EntryState (#933)
Some checks failed
Build / Tests (gcc, gcov, Linux GCC, ubuntu-latest) (push) Failing after 7m20s
Build / Tests (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Successful in 7m54s
Build / documentation (push) Failing after 1m59s
Build / Tests (cl, Windows MSVC, windows-latest) (push) Has been cancelled
Build / Create release (push) Has been cancelled
Build / Build packages (build/ftxui*Darwin*, macos-latest) (push) Has been cancelled
Build / Build packages (build/ftxui*Linux*, ubuntu-latest) (push) Has been cancelled
Build / Build packages (build/ftxui*Win64*, windows-latest) (push) Has been cancelled
CodeQL / Analyze (cpp) (push) Failing after 59s
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2024-09-30 23:18:59 +02:00
alexv-ds
baa5973128
msvc getenv deprecation warn fix 2024-07-18 18:34:34 +03:00
10 changed files with 93 additions and 28 deletions

View File

@ -35,6 +35,10 @@ current (development)
- Bugfix: Fix cursor position in when in the last column. See #831. - Bugfix: Fix cursor position in when in the last column. See #831.
- Bugfix: Fix `ResizeableSplit` keyboard navigation. Fixed by #842. - Bugfix: Fix `ResizeableSplit` keyboard navigation. Fixed by #842.
- Bugfix: Fix `Menu` focus. See #841 - Bugfix: Fix `Menu` focus. See #841
- Feature: Add `ComponentBase::Index()`. This allows to get the index of a
component in its parent. See #932
- Feature: Add `EntryState::index`. This allows to get the index of a menu entry.
See #932
### Dom ### Dom
- Feature: Add `hscroll_indicator`. It display an horizontal indicator - Feature: Add `hscroll_indicator`. It display an horizontal indicator

View File

@ -44,6 +44,7 @@ class ComponentBase {
ComponentBase* Parent() const; ComponentBase* Parent() const;
Component& ChildAt(size_t i); Component& ChildAt(size_t i);
size_t ChildCount() const; size_t ChildCount() const;
int Index() const;
void Add(Component children); void Add(Component children);
void Detach(); void Detach();
void DetachAllChildren(); void DetachAllChildren();

View File

@ -25,6 +25,7 @@ struct EntryState {
bool state; ///< The state of the button/checkbox/radiobox bool state; ///< The state of the button/checkbox/radiobox
bool active; ///< Whether the entry is the active one. bool active; ///< Whether the entry is the active one.
bool focused; ///< Whether the entry is one focused by the user. bool focused; ///< Whether the entry is one focused by the user.
int index; ///< Index of the entry when applicable or -1.
}; };
struct UnderlineOption { struct UnderlineOption {

View File

@ -48,11 +48,8 @@ class ButtonBase : public ComponentBase, public ButtonOption {
} }
auto focus_management = focused ? focus : active ? select : nothing; auto focus_management = focused ? focus : active ? select : nothing;
const EntryState state = { const EntryState state{
*label, *label, false, active, focused_or_hover, Index(),
false,
active,
focused_or_hover,
}; };
auto element = (transform ? transform : DefaultTransform) // auto element = (transform ? transform : DefaultTransform) //

View File

@ -28,10 +28,7 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
const bool is_active = Active(); const bool is_active = Active();
auto focus_management = is_focused ? focus : is_active ? select : nothing; auto focus_management = is_focused ? focus : is_active ? select : nothing;
auto entry_state = EntryState{ auto entry_state = EntryState{
*label, *label, *checked, is_active, is_focused || hovered_, -1,
*checked,
is_active,
is_focused || hovered_,
}; };
auto element = (transform ? transform : CheckboxOption::Simple().transform)( auto element = (transform ? transform : CheckboxOption::Simple().transform)(
entry_state); entry_state);

View File

@ -51,6 +51,22 @@ size_t ComponentBase::ChildCount() const {
return children_.size(); return children_.size();
} }
/// @brief Return index of the component in its parent. -1 if no parent.
/// @ingroup component
int ComponentBase::Index() const {
if (parent_ == nullptr) {
return -1;
}
int index = 0;
for (const Component& child : parent_->children_) {
if (child.get() == this) {
return index;
}
index++;
}
return -1; // Not reached.
}
/// @brief Add a child. /// @brief Add a child.
/// @@param child The child to be attached. /// @@param child The child to be attached.
/// @ingroup component /// @ingroup component

View File

@ -123,10 +123,7 @@ class MenuBase : public ComponentBase, public MenuOption {
const bool is_selected = (selected() == i); const bool is_selected = (selected() == i);
const EntryState state = { const EntryState state = {
entries[i], entries[i], false, is_selected, is_focused, i,
false,
is_selected,
is_focused,
}; };
auto focus_management = (selected_focus_ != i) ? nothing auto focus_management = (selected_focus_ != i) ? nothing
@ -625,11 +622,8 @@ Component MenuEntry(MenuEntryOption option) {
const bool focused = Focused(); const bool focused = Focused();
UpdateAnimationTarget(); UpdateAnimationTarget();
const EntryState state = { const EntryState state{
label(), label(), false, hovered_, focused, Index(),
false,
hovered_,
focused,
}; };
const Element element = const Element element =

View File

@ -226,5 +226,50 @@ TEST(MenuTest, AnimationsVertical) {
} }
} }
TEST(MenuTest, EntryIndex) {
int selected = 0;
std::vector<std::string> entries = {"0", "1", "2"};
auto option = MenuOption::Vertical();
option.entries = &entries;
option.selected = &selected;
option.entries_option.transform = [&](const EntryState& state) {
int curidx = std::stoi(state.label);
EXPECT_EQ(state.index, curidx);
return text(state.label);
};
auto menu = Menu(option);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::Return);
entries.resize(2);
(void)menu->Render();
}
TEST(MenuTest, MenuEntryIndex) {
int selected = 0;
MenuEntryOption option;
option.transform = [&](const EntryState& state) {
int curidx = std::stoi(state.label);
EXPECT_EQ(state.index, curidx);
return text(state.label);
};
auto menu = Container::Vertical(
{
MenuEntry("0", option),
MenuEntry("1", option),
MenuEntry("2", option),
},
&selected);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::Return);
for (int index = 0; index < menu->ChildCount(); index++) {
EXPECT_EQ(menu->ChildAt(index)->Index(), index);
}
}
} // namespace ftxui } // namespace ftxui
// NOLINTEND // NOLINTEND

View File

@ -40,10 +40,7 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
: is_menu_focused ? focus : is_menu_focused ? focus
: select; : select;
auto state = EntryState{ auto state = EntryState{
entries[i], entries[i], selected() == i, is_selected, is_focused, i,
selected() == i,
is_selected,
is_focused,
}; };
auto element = auto element =
(transform ? transform : RadioboxOption::Simple().transform)(state); (transform ? transform : RadioboxOption::Simple().transform)(state);

View File

@ -48,25 +48,38 @@ Dimensions& FallbackSize() {
return g_fallback_size; return g_fallback_size;
} }
const char* Safe(const char* c) {
return (c != nullptr) ? c : "";
}
bool Contains(const std::string& s, const char* key) { bool Contains(const std::string& s, const char* key) {
return s.find(key) != std::string::npos; return s.find(key) != std::string::npos;
} }
// https://github.com/gabime/spdlog/blob/885b5473e291833b148eeac3b7ce227e582cd88b/include/spdlog/details/os-inl.h#L566
std::string getenv_safe(const char *field) {
#if defined(_MSC_VER)
#if defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp
#else
size_t len = 0;
char buf[1024];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{};
#endif
#else // revert to getenv
char *buf = ::getenv(field); // NOLINT(*-mt-unsafe)
return buf ? buf : std::string{};
#endif
}
Terminal::Color ComputeColorSupport() { Terminal::Color ComputeColorSupport() {
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
return Terminal::Color::TrueColor; return Terminal::Color::TrueColor;
#endif #endif
std::string COLORTERM = Safe(std::getenv("COLORTERM")); // NOLINT std::string COLORTERM = getenv_safe("COLORTERM");
if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) { if (Contains(COLORTERM, "24bit") || Contains(COLORTERM, "truecolor")) {
return Terminal::Color::TrueColor; return Terminal::Color::TrueColor;
} }
std::string TERM = Safe(std::getenv("TERM")); // NOLINT std::string TERM = getenv_safe("TERM");
if (Contains(COLORTERM, "256") || Contains(TERM, "256")) { if (Contains(COLORTERM, "256") || Contains(TERM, "256")) {
return Terminal::Color::Palette256; return Terminal::Color::Palette256;
} }