diff --git a/CHANGELOG.md b/CHANGELOG.md index 120dd62..cfe7ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,9 @@ current (development) ### Screen - Feature: Add `Box::IsEmpty()`. +### Util +- Feature: Support arbitrary `Adapter` for `ConstStringListRef`. See #843. + ### Build - Support for cmake's "unity/jumbo" builds. Fixed by @ClausKlein. diff --git a/cmake/ftxui_test.cmake b/cmake/ftxui_test.cmake index 0b9a86d..c75ea43 100644 --- a/cmake/ftxui_test.cmake +++ b/cmake/ftxui_test.cmake @@ -18,6 +18,7 @@ add_executable(ftxui-tests src/ftxui/component/menu_test.cpp src/ftxui/component/modal_test.cpp src/ftxui/component/radiobox_test.cpp + src/ftxui/util/ref_test.cpp src/ftxui/component/receiver_test.cpp src/ftxui/component/resizable_split_test.cpp src/ftxui/component/screen_interactive_test.cpp diff --git a/include/ftxui/util/ref.hpp b/include/ftxui/util/ref.hpp index 15e03cf..42a5938 100644 --- a/include/ftxui/util/ref.hpp +++ b/include/ftxui/util/ref.hpp @@ -5,8 +5,10 @@ #define FTXUI_UTIL_REF_HPP #include +#include #include #include +#include namespace ftxui { @@ -104,42 +106,109 @@ class ConstStringRef : public ConstRef { }; /// @brief An adapter. Reference a list of strings. +/// +/// Supported input: +/// - `std::vector` +/// - `std::vector*` +/// - `std::vector*` +/// - `Adapter*` +/// - `std::unique_ptr` class ConstStringListRef { public: + // Bring your own adapter: + class Adapter { + public: + Adapter() = default; + Adapter(const Adapter&) = default; + Adapter& operator=(const Adapter&) = default; + Adapter(Adapter&&) = default; + Adapter& operator=(Adapter&&) = default; + virtual ~Adapter() = default; + virtual size_t size() const = 0; + virtual std::string operator[](size_t i) const = 0; + }; + using Variant = std::variant, // + const std::vector*, // + const std::vector*, // + Adapter*, // + std::unique_ptr // + >; + ConstStringListRef() = default; ~ConstStringListRef() = default; - ConstStringListRef(ConstStringListRef&&) = delete; - ConstStringListRef& operator=(ConstStringListRef&&) = delete; - ConstStringListRef(const std::vector* ref) // NOLINT - : ref_(ref) {} - ConstStringListRef(const std::vector* ref) // NOLINT - : ref_wide_(ref) {} - ConstStringListRef(const ConstStringListRef& other) = default; - ConstStringListRef& operator=(const ConstStringListRef& other) = default; + ConstStringListRef& operator=(const ConstStringListRef&) = default; + ConstStringListRef& operator=(ConstStringListRef&&) = default; + ConstStringListRef(ConstStringListRef&&) = default; + ConstStringListRef(const ConstStringListRef&) = default; + + ConstStringListRef(std::vector value) // NOLINT + { + variant_ = std::make_shared(value); + } + ConstStringListRef(const std::vector* value) // NOLINT + { + variant_ = std::make_shared(value); + } + ConstStringListRef(const std::vector* value) // NOLINT + { + variant_ = std::make_shared(value); + } + ConstStringListRef(Adapter* adapter) // NOLINT + { + variant_ = std::make_shared(adapter); + } + template + ConstStringListRef(std::unique_ptr adapter) // NOLINT + { + variant_ = std::make_shared( + static_cast>(std::move(adapter))); + } size_t size() const { - if (ref_) { - return ref_->size(); - } - if (ref_wide_) { - return ref_wide_->size(); - } - return 0; + return variant_ ? std::visit(SizeVisitor(), *variant_) : 0; } std::string operator[](size_t i) const { - if (ref_) { - return (*ref_)[i]; - } - if (ref_wide_) { - return to_string((*ref_wide_)[i]); - } - return ""; + return variant_ ? std::visit(IndexedGetter(i), *variant_) : ""; } private: - const std::vector* ref_ = nullptr; - const std::vector* ref_wide_ = nullptr; + struct SizeVisitor { + size_t operator()(const std::vector& v) const { + return v.size(); + } + size_t operator()(const std::vector* v) const { + return v->size(); + } + size_t operator()(const std::vector* v) const { + return v->size(); + } + size_t operator()(const Adapter* v) const { return v->size(); } + size_t operator()(const std::unique_ptr& v) const { + return v->size(); + } + }; + + struct IndexedGetter { + IndexedGetter(size_t index) // NOLINT + : index_(index) {} + size_t index_; + std::string operator()(const std::vector& v) const { + return v[index_]; + } + std::string operator()(const std::vector* v) const { + return (*v)[index_]; + } + std::string operator()(const std::vector* v) const { + return to_string((*v)[index_]); + } + std::string operator()(const Adapter* v) const { return (*v)[index_]; } + std::string operator()(const std::unique_ptr& v) const { + return (*v)[index_]; + } + }; + + std::shared_ptr variant_; }; } // namespace ftxui diff --git a/src/ftxui/util/ref_test.cpp b/src/ftxui/util/ref_test.cpp new file mode 100644 index 0000000..e21dc56 --- /dev/null +++ b/src/ftxui/util/ref_test.cpp @@ -0,0 +1,63 @@ +#include "ftxui/util/ref.hpp" + +#include +#include "ftxui/component/component.hpp" + +namespace ftxui { +namespace { +class Adapter : public ConstStringListRef::Adapter { + public: + Adapter(std::vector& entries) : entries(entries) {} + size_t size() const override { return entries.size() * 2; } + std::string operator[](size_t index) const override { + return entries[index / 2]; + } + std::vector& entries; +}; +} // namespace + +TEST(ConstStringListRef, Copy) { + std::vector entries = { + "entry 1", + "entry 2", + "entry 3", + }; + int selected = 0; + auto menu = Menu(entries, &selected); +} + +TEST(ConstStringListRef, Ref) { + std::vector entries = { + "entry 1", + "entry 2", + "entry 3", + }; + int selected = 0; + auto menu = Menu(&entries, &selected); +} + +TEST(ConstStringListRef, Adapter) { + std::vector entries = { + "entry 1", + "entry 2", + "entry 3", + }; + + int selected = 0; + Adapter a(entries); + auto menu = Menu(&a, &selected); +} + +TEST(ConstStringListRef, UniquePtrAdapter) { + std::vector entries = { + "entry 1", + "entry 2", + "entry 3", + }; + + int selected = 0; + auto a = std::make_unique(entries); + auto menu = Menu(std::move(a), &selected); +} + +} // namespace ftxui