Prefer std::string over std::wstring. (#179)

In the past, FTXUI switched from std::string to std::wstring to support
fullwidth characters. The reasons was that fullwidth characters can be
stored inside a single wchar_t.

Then FTXUI added support for combining characters. A single glygh
doesn't even fit a wchar_t. Instead, a glyph can be arbitrary large.

The usage of wstring doesn't really fit the new model and have several
drawbacks:
1. It doesn't simplify the implementation of FTXUI, because of combining
   characters.
2. It reduces drawing performance by 2x.
3. It increase Screen's memory allocation by 2x.

This patch converts FTXUI to use std::string internally. It now exposes
std::string based API. The std::wstring API remains, but is now
deprecated.

Tests and examples haven't been update to show the breakage is limited.
They will be updated in a second set of patches.

Bug: https://github.com/ArthurSonzogni/FTXUI/issues/153
Co-authored-by: Tushar Maheshwari <tushar27192@gmail.com>
This commit is contained in:
Arthur Sonzogni
2021-08-08 23:25:20 +02:00
committed by GitHub
parent 1ff894f6f5
commit 3b4ab618a3
84 changed files with 1234 additions and 996 deletions

View File

@@ -75,7 +75,7 @@ class ButtonBase : public ComponentBase {
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring label = L"Click to quit";
/// std::string label = "Click to quit";
/// Component button = Button(&label, screen.ExitLoopClosure());
/// screen.Loop(button)
/// ```

View File

@@ -37,7 +37,7 @@ class CatchEventBase : public ComponentBase {
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// auto renderer = Renderer([] {
/// return text(L"My interface");
/// return text("My interface");
/// });
/// screen.Loop(renderer);
/// ```

View File

@@ -22,10 +22,10 @@ class CheckboxBase : public ComponentBase {
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminal do not use fonts able to render properly the default
// radiobox glyph.
if (option_->style_checked == L"")
option_->style_checked = L"[X]";
if (option_->style_unchecked == L"")
option_->style_unchecked = L"[ ]";
if (option_->style_checked == "")
option_->style_checked = "[X]";
if (option_->style_unchecked == "")
option_->style_unchecked = "[ ]";
#endif
}
@@ -91,7 +91,7 @@ class CheckboxBase : public ComponentBase {
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring label = L"Make a sandwidth";
/// std::string label = "Make a sandwidth";
/// bool checked = false;
/// Component checkbox = Checkbox(&label, &checked);
/// screen.Loop(checkbox)

View File

@@ -3,6 +3,7 @@
#include <cassert> // for assert
#include <iterator> // for begin, end
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component.hpp"
@@ -82,7 +83,7 @@ Element ComponentBase::Render() {
if (children_.size() == 1)
return children_.front()->Render();
return text(L"Not implemented component");
return text("Not implemented component");
}
/// @brief Called in response to an event.

View File

@@ -93,7 +93,7 @@ class VerticalContainer : public ContainerBase {
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return text("Empty container");
return vbox(std::move(elements));
}
@@ -122,7 +122,7 @@ class HorizontalContainer : public ContainerBase {
for (auto& it : children_)
elements.push_back(it->Render());
if (elements.size() == 0)
return text(L"Empty container");
return text("Empty container");
return hbox(std::move(elements));
}
@@ -150,7 +150,7 @@ class TabContainer : public ContainerBase {
Component active_child = ActiveChild();
if (active_child)
return active_child->Render();
return text(L"Empty container");
return text("Empty container");
}
bool OnMouseEvent(Event event) override {

View File

@@ -9,7 +9,6 @@ namespace ftxui {
// static
Event Event::Character(std::string input) {
Event event;
event.character_ = to_wstring(input)[0];
event.input_ = std::move(input);
event.type_ = Type::Character;
return event;
@@ -17,16 +16,12 @@ Event Event::Character(std::string input) {
// static
Event Event::Character(char c) {
return Character(wchar_t(c));
return Event::Character(std::string{c});
}
// static
Event Event::Character(wchar_t c) {
Event event;
event.input_ = {(char)c};
event.type_ = Type::Character;
event.character_ = c;
return event;
return Event::Character(to_string(std::wstring{c}));
}
// static

View File

@@ -8,21 +8,24 @@
#include "ftxui/component/component.hpp" // for Make, Input
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/deprecated.hpp" // for Input
#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/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" // for operator|, Element, text, reflect, dim, flex, focus, inverted, hbox, size, frame, select, underlined, Decorator, EQUAL, HEIGHT
#include "ftxui/dom/deprecated.hpp" // for text
#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, text, dim, flex, focus, inverted, hbox, size, frame, select, underlined, Decorator, EQUAL, HEIGHT
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for StringRef, Ref, ConstStringRef
#include "ftxui/screen/string.hpp" // for to_wstring, to_string
#include "ftxui/util/ref.hpp" // for WideStringRef, Ref, ConstStringRef, StringRef
namespace ftxui {
// An input box. The user can type text into it.
class InputBase : public ComponentBase {
class WideInputBase : public ComponentBase {
public:
InputBase(StringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
WideInputBase(WideStringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
: content_(content), placeholder_(placeholder), option_(option) {}
int& cursor_position() { return *(option_->cursor_position); }
@@ -132,7 +135,7 @@ class InputBase : public ComponentBase {
// Content
if (event.is_character()) {
content_->insert(cursor_position(), 1, event.character());
content_->insert(cursor_position(), 1, to_wstring(event.character())[0]);
cursor_position()++;
option_->on_change();
return true;
@@ -165,7 +168,7 @@ class InputBase : public ComponentBase {
bool Focusable() const final { return true; }
StringRef content_;
WideStringRef content_;
ConstStringRef placeholder_;
Box input_box_;
@@ -173,6 +176,43 @@ class InputBase : public ComponentBase {
Ref<InputOption> option_;
};
// An input box. The user can type text into it.
// For convenience, the std::string version of Input simply wrap a
// WideInputBase.
// TODO(arthursonzogni): Provide an implementation handling std::string natively
// and adds better support for combining characters.
class InputBase : public ComponentBase {
public:
InputBase(StringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
: content_(std::move(content)),
wrapped_content_(to_wstring(*content_)),
wrapped_input_(&wrapped_content_,
std::move(placeholder),
std::move(option)) {}
Element Render() override {
wrapped_content_ = to_wstring(*content_);
return wrapped_input_.Render();
}
bool OnEvent(Event event) override {
wrapped_content_ = to_wstring(*content_);
if (wrapped_input_.OnEvent(event)) {
content_ = to_string(wrapped_content_);
return true;
}
return false;
}
bool Focusable() const final { return true; }
StringRef content_;
std::wstring wrapped_content_;
WideInputBase wrapped_input_;
};
/// @brief An input box for editing text.
/// @param content The editable content.
/// @param placeholder The text displayed when content is still empty.
@@ -184,8 +224,8 @@ class InputBase : public ComponentBase {
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::wstring content= L"";
/// std::wstring placeholder = L"placeholder";
/// std::string content= "";
/// std::string placeholder = "placeholder";
/// Component input = Input(&content, &placeholder);
/// screen.Loop(input);
/// ```
@@ -201,6 +241,34 @@ Component Input(StringRef content,
return Make<InputBase>(content, placeholder, std::move(option));
}
/// @brief . An input box for editing text.
/// @param content The editable content.
/// @param placeholder The text displayed when content is still empty.
/// @param option Additional optional parameters.
/// @ingroup component
/// @see InputBase
///
/// ### Example
///
/// ```cpp
/// auto screen = ScreenInteractive::FitComponent();
/// std::string content= "";
/// std::string placeholder = "placeholder";
/// Component input = Input(&content, &placeholder);
/// screen.Loop(input);
/// ```
///
/// ### Output
///
/// ```bash
/// placeholder
/// ```
Component Input(WideStringRef content,
ConstStringRef placeholder,
Ref<InputOption> option) {
return Make<WideInputBase>(content, placeholder, std::move(option));
}
} // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@@ -4,9 +4,9 @@
#include <string> // for wstring
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Input
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/deprecated.hpp" // for Input
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Delete, Event::End, Event::Home
#include "ftxui/dom/elements.hpp" // for Fit
#include "ftxui/dom/node.hpp" // for Render
@@ -42,8 +42,8 @@ TEST(InputTest, Type) {
auto document = input->Render();
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
EXPECT_EQ(screen.PixelAt(0, 0).character, L"a");
EXPECT_EQ(screen.PixelAt(1, 0).character, L"b");
EXPECT_EQ(screen.PixelAt(0, 0).character, "a");
EXPECT_EQ(screen.PixelAt(1, 0).character, "b");
}
TEST(InputTest, TypePassword) {
@@ -64,8 +64,8 @@ TEST(InputTest, TypePassword) {
auto document = input->Render();
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
EXPECT_EQ(screen.PixelAt(0, 0).character, L"");
EXPECT_EQ(screen.PixelAt(1, 0).character, L"");
EXPECT_EQ(screen.PixelAt(0, 0).character, "");
EXPECT_EQ(screen.PixelAt(1, 0).character, "");
}
TEST(InputTest, Arrow) {

View File

@@ -2,7 +2,7 @@
#include <algorithm> // for max, min
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for operator+, wstring
#include <string> // for operator+, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
@@ -23,16 +23,14 @@ namespace ftxui {
/// @ingroup component
class MenuBase : public ComponentBase {
public:
MenuBase(const std::vector<std::wstring>* entries,
int* selected,
Ref<MenuOption> option)
MenuBase(ConstStringListRef entries, int* selected, Ref<MenuOption> option)
: entries_(entries), selected_(selected), option_(option) {}
Element 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_entry() == int(i)) && is_menu_focused;
bool is_selected = (*selected_ == int(i));
@@ -43,9 +41,9 @@ class MenuBase : public ComponentBase {
auto focus_management = !is_selected ? nothing
: is_menu_focused ? focus
: select;
auto icon = is_selected ? L"> " : L" ";
elements.push_back(text(icon + entries_->at(i)) | style |
focus_management | reflect(boxes_[i]));
auto icon = is_selected ? "> " : " ";
elements.push_back(text(icon + entries_[i]) | style | focus_management |
reflect(boxes_[i]));
}
return vbox(std::move(elements));
}
@@ -64,12 +62,12 @@ class MenuBase : public ComponentBase {
(*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();
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_entry() = *selected_;
@@ -106,12 +104,11 @@ class MenuBase : public ComponentBase {
return false;
}
bool Focusable() const final { return entries_->size(); }
bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); }
protected:
const std::vector<std::wstring>* const entries_;
ConstStringListRef entries_;
int* selected_ = 0;
Ref<MenuOption> option_;
@@ -129,10 +126,10 @@ class MenuBase : public ComponentBase {
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::vector<std::wstring> entries = {
/// L"entry 1",
/// L"entry 2",
/// L"entry 3",
/// std::vector<std::string> entries = {
/// "entry 1",
/// "entry 2",
/// "entry 3",
/// };
/// int selected = 0;
/// auto menu = Menu(&entries, &selected);
@@ -146,7 +143,7 @@ class MenuBase : public ComponentBase {
/// entry 2
/// entry 3
/// ```
Component Menu(const std::vector<std::wstring>* entries,
Component Menu(ConstStringListRef entries,
int* selected,
Ref<MenuOption> option) {
return Make<MenuBase>(entries, selected, std::move(option));

View File

@@ -2,7 +2,7 @@
#include <algorithm> // for max, min
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for wstring
#include <string> // for string
#include <utility> // for move
#include <vector> // for vector
@@ -25,26 +25,26 @@ namespace {
/// @ingroup component
class RadioboxBase : public ComponentBase {
public:
RadioboxBase(const std::vector<std::wstring>* entries,
RadioboxBase(ConstStringListRef entries,
int* selected,
Ref<RadioboxOption> option)
: entries_(entries), selected_(selected), option_(std::move(option)) {
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft terminal do not use fonts able to render properly the default
// radiobox glyph.
if (option_->style_checked == L"")
option_->style_checked = L"(*)";
if (option_->style_unchecked == L"")
option_->style_unchecked = L"( )";
if (option_->style_checked == "")
option_->style_checked = "(*)";
if (option_->style_unchecked == "")
option_->style_unchecked = "( )";
#endif
}
private:
Element Render() override {
std::vector<Element> elements;
Elements 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_entry() == int(i) && is_focused)
? option_->style_focused
: option_->style_unfocused;
@@ -52,10 +52,10 @@ class RadioboxBase : public ComponentBase {
: is_focused ? focus
: select;
const std::wstring& symbol = *selected_ == int(i)
? option_->style_checked
: option_->style_unchecked;
elements.push_back(hbox(text(symbol), text(entries_->at(i)) | style) |
const std::string& symbol = *selected_ == int(i)
? option_->style_checked
: option_->style_unchecked;
elements.push_back(hbox(text(symbol), text(entries_[i]) | style) |
focus_management | reflect(boxes_[i]));
}
return vbox(std::move(elements));
@@ -75,12 +75,12 @@ class RadioboxBase : public ComponentBase {
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_entry() != new_focused) {
focused_entry() = new_focused;
@@ -119,11 +119,10 @@ class RadioboxBase : public ComponentBase {
return false;
}
bool Focusable() const final { return entries_->size(); }
bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); }
const std::vector<std::wstring>* const entries_;
ConstStringListRef entries_;
int* const selected_;
int cursor_position = 0;
@@ -144,10 +143,10 @@ class RadioboxBase : public ComponentBase {
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::vector<std::wstring> entries = {
/// L"entry 1",
/// L"entry 2",
/// L"entry 3",
/// std::vector<std::string> entries = {
/// "entry 1",
/// "entry 2",
/// "entry 3",
/// };
/// int selected = 0;
/// auto menu = Radiobox(&entries, &selected);
@@ -161,7 +160,7 @@ class RadioboxBase : public ComponentBase {
/// ○ entry 2
/// ○ entry 3
/// ```
Component Radiobox(const std::vector<std::wstring>* entries,
Component Radiobox(ConstStringListRef entries,
int* selected,
Ref<RadioboxOption> option) {
return Make<RadioboxBase>(entries, selected, std::move(option));

View File

@@ -21,7 +21,7 @@ namespace ftxui {
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// auto renderer = Renderer([] {
/// return text(L"My interface");
/// return text("My interface");
/// });
/// screen.Loop(renderer);
/// ```
@@ -46,7 +46,7 @@ Component Renderer(std::function<Element()> render) {
///
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// std::wstring label = "Click to quit";
/// std::string label = "Click to quit";
/// auto button = Button(&label, screen.ExitLoopClosure());
/// auto renderer = Renderer(button, [&] {
/// return hbox({

View File

@@ -250,8 +250,8 @@ class ResizableSplitBottomBase : public ComponentBase {
/// ```cpp
/// auto screen = ScreenInteractive::Fullscreen();
/// int left_size = 10;
/// auto left = Renderer([] { return text(L"Left") | center;});
/// auto right = Renderer([] { return text(L"right") | center;});
/// auto left = Renderer([] { return text("Left") | center;});
/// auto right = Renderer([] { return text("right") | center;});
/// auto split = ResizableSplitLeft(left, right, &left_size);
/// screen.Loop(split);
/// ```
@@ -280,8 +280,8 @@ Component ResizableSplitLeft(Component main, Component back, int* main_size) {
/// ```cpp
/// auto screen = ScreenInteractive::Fullscreen();
/// int right_size = 10;
/// auto left = Renderer([] { return text(L"Left") | center;});
/// auto right = Renderer([] { return text(L"right") | center;});
/// auto left = Renderer([] { return text("Left") | center;});
/// auto right = Renderer([] { return text("right") | center;});
/// auto split = ResizableSplitRight(right, left, &right_size);
/// screen.Loop(split);
/// ```
@@ -310,8 +310,8 @@ Component ResizableSplitRight(Component main, Component back, int* main_size) {
/// ```cpp
/// auto screen = ScreenInteractive::Fullscreen();
/// int top_size = 1;
/// auto top = Renderer([] { return text(L"Top") | center;});
/// auto bottom = Renderer([] { return text(L"Bottom") | center;});
/// auto top = Renderer([] { return text("Top") | center;});
/// auto bottom = Renderer([] { return text("Bottom") | center;});
/// auto split = ResizableSplitTop(top, bottom, &top_size);
/// screen.Loop(split);
/// ```
@@ -340,8 +340,8 @@ Component ResizableSplitTop(Component main, Component back, int* main_size) {
/// ```cpp
/// auto screen = ScreenInteractive::Fullscreen();
/// int bottom_size = 1;
/// auto top = Renderer([] { return text(L"Top") | center;});
/// auto bottom = Renderer([] { return text(L"Bottom") | center;});
/// auto top = Renderer([] { return text("Top") | center;});
/// auto bottom = Renderer([] { return text("Bottom") | center;});
/// auto split = ResizableSplit::Bottom(bottom, top, &bottom_size);
/// screen.Loop(split);
/// ```

View File

@@ -1,11 +1,12 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult
#include <csignal>
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <csignal> // for raise, SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
#include "ftxui/component/component.hpp"
#include "ftxui/component/component.hpp" // for Renderer
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/elements.hpp"
#include "gtest/gtest_pred_impl.h" // for AssertionResult, Test, EXPECT_EQ
#include "ftxui/dom/deprecated.hpp" // for text
#include "ftxui/dom/elements.hpp" // for Element
#include "gtest/gtest_pred_impl.h" // for Test, TEST, EXPECT_EQ
using namespace ftxui;

View File

@@ -17,7 +17,7 @@ namespace ftxui {
template <class T>
class SliderBase : public ComponentBase {
public:
SliderBase(StringRef label, T* value, T min, T max, T increment)
SliderBase(ConstStringRef label, T* value, T min, T max, T increment)
: label_(label),
value_(value),
min_(min),
@@ -31,9 +31,9 @@ class SliderBase : public ComponentBase {
return hbox({
text(*label_) | dim | vcenter,
hbox({
text(L"["),
text("["),
gauge(percent) | underlined | xflex | reflect(gauge_box_),
text(L"]"),
text("]"),
}) | xflex,
}) |
gauge_color | xflex | reflect(box_);
@@ -87,7 +87,7 @@ class SliderBase : public ComponentBase {
bool Focusable() const final { return true; }
private:
StringRef label_;
ConstStringRef label_;
T* value_;
T min_;
T max_;
@@ -110,7 +110,7 @@ class SliderBase : public ComponentBase {
/// ```cpp
/// auto screen = ScreenInteractive::TerminalOutput();
/// int value = 50;
/// auto slider = Slider(L"Value:", &value, 0, 100, 1);
/// auto slider = Slider("Value:", &value, 0, 100, 1);
/// screen.Loop(slider);
/// ```
///
@@ -120,23 +120,23 @@ class SliderBase : public ComponentBase {
/// Value:[██████████████████████████ ]
/// ```
template <class T>
Component Slider(StringRef label, T* value, T min, T max, T increment) {
Component Slider(ConstStringRef label, T* value, T min, T max, T increment) {
return Make<SliderBase<T>>(std::move(label), value, min, max, increment);
}
template Component Slider(StringRef label,
template Component Slider(ConstStringRef label,
int* value,
int min,
int max,
int increment);
template Component Slider(StringRef label,
template Component Slider(ConstStringRef label,
float* value,
float min,
float max,
float increment);
template Component Slider(StringRef label,
template Component Slider(ConstStringRef label,
long* value,
long min,
long max,

View File

@@ -29,7 +29,7 @@ TEST(Event, Character) {
for (char c : basic_char) {
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_TRUE(received.is_character());
EXPECT_EQ(c, received.character());
EXPECT_EQ(c, received.character()[0]);
}
EXPECT_FALSE(event_receiver->Receive(&received));
}

View File

@@ -2,19 +2,18 @@
#include <algorithm> // for max, min
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for wstring
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Component, Toggle
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Toggle
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for ToggleOption
#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/dom/elements.hpp" // for operator|, Element, Elements, hbox, reflect, separator, text, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for Ref
#include "ftxui/util/ref.hpp" // for ConstStringListRef, Ref
namespace ftxui {
@@ -24,7 +23,7 @@ namespace {
/// @ingroup component
class ToggleBase : public ComponentBase {
public:
ToggleBase(const std::vector<std::wstring>* entries,
ToggleBase(ConstStringListRef entries,
int* selected,
Ref<ToggleOption> option)
: entries_(entries), selected_(selected), option_(std::move(option)) {}
@@ -33,8 +32,8 @@ class ToggleBase : public ComponentBase {
Element Render() override {
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());
@@ -49,7 +48,7 @@ class ToggleBase : public ComponentBase {
auto focus_management = !is_selected ? nothing
: is_toggle_focused ? focus
: select;
children.push_back(text(entries_->at(i)) | style | focus_management |
children.push_back(text(entries_[i]) | style | focus_management |
reflect(boxes_[i]));
}
return hbox(std::move(children));
@@ -64,12 +63,12 @@ class ToggleBase : public ComponentBase {
(*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();
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_entry() = *selected_;
@@ -107,11 +106,10 @@ class ToggleBase : public ComponentBase {
return false;
}
bool Focusable() const final { return entries_->size(); }
bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); }
const std::vector<std::wstring>* const entries_;
ConstStringListRef entries_;
int* selected_ = 0;
std::vector<Box> boxes_;
@@ -125,7 +123,7 @@ class ToggleBase : public ComponentBase {
/// @param selected Reference the selected entry.
/// @param option Additional optional parameters.
/// @ingroup component
Component Toggle(const std::vector<std::wstring>* entries,
Component Toggle(ConstStringListRef entries,
int* selected,
Ref<ToggleOption> option) {
return Make<ToggleBase>(entries, selected, std::move(option));