Skip to content

Commit

Permalink
Add ConstStringListRef Adapter.
Browse files Browse the repository at this point in the history
Bug:#843
  • Loading branch information
ArthurSonzogni committed Apr 28, 2024
1 parent 6a755f3 commit 3791066
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 18 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions cmake/ftxui_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 79 additions & 18 deletions include/ftxui/util/ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
#define FTXUI_UTIL_REF_HPP

#include <ftxui/screen/string.hpp>
#include <functional>
#include <memory>
#include <string>
#include <variant>
#include <vector>

namespace ftxui {

Expand Down Expand Up @@ -95,37 +98,95 @@ 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:
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(const std::vector<std::string>* ref) : ref_(ref) {}
ConstStringListRef(const std::vector<std::wstring>* ref) : ref_wide_(ref) {}
ConstStringListRef(std::vector<std::string> value) {
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(const std::vector<std::string>* value) {
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(const std::vector<std::wstring>* value) {
variant_ = std::make_shared<Variant>(value);
}
ConstStringListRef(Adapter* adapter) {
variant_ = std::make_shared<Variant>(adapter);
}
template <typename AdapterType>
ConstStringListRef(std::unique_ptr<AdapterType> adapter) {
variant_ = std::make_shared<Variant>(
static_cast<std::unique_ptr<Adapter>>(std::move(adapter)));
}

ConstStringListRef(const ConstStringListRef& other) = default;
ConstStringListRef& operator=(const ConstStringListRef& other) = default;

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) : 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
Expand Down
63 changes: 63 additions & 0 deletions src/ftxui/util/ref_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "ftxui/util/ref.hpp"

#include <gtest/gtest.h>
#include "ftxui/component/component.hpp"

namespace ftxui {
namespace {
class A : public ConstStringListRef::Adapter {
public:
A(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;
A 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<A>(entries);
auto menu = Menu(std::move(a), &selected);
}

} // namespace ftxui

0 comments on commit 3791066

Please sign in to comment.