diff --git a/CHANGELOG.md b/CHANGELOG.md index 120dd62579b468747a2b4646a903ea6ff88e9748..cfe7ad694d9b777edb94b1aa1eb6c684e693d263 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 0b9a86df333d7757e6f530201a3ed3d58ffe756f..c75ea4316c760433fa05097c59b265fb2d225212 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 15e03cf740c99d493b61722c8f963939abfe2106..42a5938149b77176c536102c3f08edc028287f9d 100644 --- a/include/ftxui/util/ref.hpp +++ b/include/ftxui/util/ref.hpp @@ -5,8 +5,10 @@ #define FTXUI_UTIL_REF_HPP #include <ftxui/screen/string.hpp> +#include <memory> #include <string> #include <variant> +#include <vector> namespace ftxui { @@ -104,42 +106,109 @@ class ConstStringRef : public ConstRef<std::string> { }; /// @brief An adapter. Reference a list of strings. +/// +/// Supported input: +/// - `std::vector<std::string>` +/// - `std::vector<std::string>*` +/// - `std::vector<std::wstring>*` +/// - `Adapter*` +/// - `std::unique_ptr<Adapter>` 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<std::string>, // + const std::vector<std::string>*, // + const std::vector<std::wstring>*, // + Adapter*, // + std::unique_ptr<Adapter> // + >; + ConstStringListRef() = default; ~ConstStringListRef() = default; - ConstStringListRef(ConstStringListRef&&) = delete; - ConstStringListRef& operator=(ConstStringListRef&&) = delete; - ConstStringListRef(const std::vector<std::string>* ref) // NOLINT - : ref_(ref) {} - ConstStringListRef(const std::vector<std::wstring>* 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<std::string> value) // NOLINT + { + variant_ = std::make_shared<Variant>(value); + } + ConstStringListRef(const std::vector<std::string>* value) // NOLINT + { + variant_ = std::make_shared<Variant>(value); + } + ConstStringListRef(const std::vector<std::wstring>* value) // NOLINT + { + variant_ = std::make_shared<Variant>(value); + } + ConstStringListRef(Adapter* adapter) // NOLINT + { + variant_ = std::make_shared<Variant>(adapter); + } + template <typename AdapterType> + ConstStringListRef(std::unique_ptr<AdapterType> adapter) // NOLINT + { + variant_ = std::make_shared<Variant>( + static_cast<std::unique_ptr<Adapter>>(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<std::string>* ref_ = nullptr; - const std::vector<std::wstring>* ref_wide_ = nullptr; + struct SizeVisitor { + size_t operator()(const std::vector<std::string>& v) const { + return v.size(); + } + size_t operator()(const std::vector<std::string>* v) const { + return v->size(); + } + size_t operator()(const std::vector<std::wstring>* v) const { + return v->size(); + } + size_t operator()(const Adapter* v) const { return v->size(); } + size_t operator()(const std::unique_ptr<Adapter>& v) const { + return v->size(); + } + }; + + struct IndexedGetter { + IndexedGetter(size_t index) // NOLINT + : index_(index) {} + size_t index_; + std::string operator()(const std::vector<std::string>& v) const { + return v[index_]; + } + std::string operator()(const std::vector<std::string>* v) const { + return (*v)[index_]; + } + std::string operator()(const std::vector<std::wstring>* v) const { + return to_string((*v)[index_]); + } + std::string operator()(const Adapter* v) const { return (*v)[index_]; } + std::string operator()(const std::unique_ptr<Adapter>& v) const { + return (*v)[index_]; + } + }; + + std::shared_ptr<Variant> variant_; }; } // namespace ftxui diff --git a/src/ftxui/util/ref_test.cpp b/src/ftxui/util/ref_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e21dc56e8113aa9e4ebc29d5eefd5df36744a14c --- /dev/null +++ b/src/ftxui/util/ref_test.cpp @@ -0,0 +1,63 @@ +#include "ftxui/util/ref.hpp" + +#include <gtest/gtest.h> +#include "ftxui/component/component.hpp" + +namespace ftxui { +namespace { +class Adapter : public ConstStringListRef::Adapter { + public: + Adapter(std::vector<std::string>& 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<std::string>& entries; +}; +} // namespace + +TEST(ConstStringListRef, Copy) { + std::vector<std::string> entries = { + "entry 1", + "entry 2", + "entry 3", + }; + int selected = 0; + auto menu = Menu(entries, &selected); +} + +TEST(ConstStringListRef, Ref) { + std::vector<std::string> entries = { + "entry 1", + "entry 2", + "entry 3", + }; + int selected = 0; + auto menu = Menu(&entries, &selected); +} + +TEST(ConstStringListRef, Adapter) { + std::vector<std::string> entries = { + "entry 1", + "entry 2", + "entry 3", + }; + + int selected = 0; + Adapter a(entries); + auto menu = Menu(&a, &selected); +} + +TEST(ConstStringListRef, UniquePtrAdapter) { + std::vector<std::string> entries = { + "entry 1", + "entry 2", + "entry 3", + }; + + int selected = 0; + auto a = std::make_unique<Adapter>(entries); + auto menu = Menu(std::move(a), &selected); +} + +} // namespace ftxui