Skip to content

Commit

Permalink
Data bindings: Allow custom getter/setter on enum types (#699)
Browse files Browse the repository at this point in the history
  • Loading branch information
AmaiKinono authored Oct 12, 2024
1 parent 3333f99 commit 02494ca
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 3 deletions.
3 changes: 2 additions & 1 deletion Include/RmlUi/Core/DataModelHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ class RMLUICORE_API DataModelConstructor {
template <typename T>
inline bool DataModelConstructor::RegisterScalar(DataTypeGetFunc<T> get_func, DataTypeSetFunc<T> set_func)
{
static_assert(!is_builtin_data_scalar<T>::value,
// Though enum is builtin scalar type, we allow custom getter/setter on it.
static_assert(!is_builtin_data_scalar<T>::value || std::is_enum<T>::value,
"Cannot register scalar data type function. Arithmetic types and String are handled internally and does not need to be registered.");
const FamilyId id = Family<T>::Id();

Expand Down
104 changes: 102 additions & 2 deletions Tests/Source/UnitTests/DataBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ static const String document_rml = R"(
<p>{{ s3.val }}</p>
<p>{{ s4.val }}</p>
<p>{{ s5.val }}</p>
<p>{{ simple }}</p>
<p>{{ scoped }}</p>
<p id="simple">{{ simple }}</p>
<p id="simple_custom">{{ simple_custom }}</p>
<p id="scoped">{{ scoped }}</p>
<p id="scoped_custom">{{ scoped_custom }}</p>
<h1>Basic</h1>
<p>{{ basic.a }}</p>
Expand Down Expand Up @@ -210,16 +212,22 @@ struct StringWrap {

enum SimpleEnum { Simple_Zero = 0, Simple_One, Simple_Two };

enum SimpleEnumCustom { Simple_Zero_Custom, Simple_One_Custom, Simple_Two_Custom };

enum class ScopedEnum : uint64_t { Zero = 0, One, Two };

enum class ScopedEnumCustom { Zero, One, Two };

struct Globals {
int i0 = 0;
int* i1 = new int(1);
UniquePtr<int> i2 = MakeUnique<int>(2);
SharedPtr<int> i3 = MakeShared<int>(3);

SimpleEnum simple = Simple_One;
SimpleEnumCustom simple_custom = Simple_One_Custom;
ScopedEnum scoped = ScopedEnum::One;
ScopedEnumCustom scoped_custom = ScopedEnumCustom::One;

String s0 = "s0";
String* s1 = new String("s1");
Expand Down Expand Up @@ -371,6 +379,84 @@ bool InitializeDataBindings(Context* context)
handle.RegisterMember("val", &StringWrap::val);
}

constructor.RegisterScalar<SimpleEnumCustom>(
[](const SimpleEnumCustom& value, Rml::Variant& variant) {
if (value == Simple_Zero_Custom)
{
variant = "Zero";
}
else if (value == Simple_One_Custom)
{
variant = "One";
}
else if (value == Simple_Two_Custom)
{
variant = "Two";
}
else
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Invalid value for SimpleEnumCustom type.");
}
},
[](SimpleEnumCustom& value, const Rml::Variant& variant) {
Rml::String str = variant.Get<Rml::String>();
if (str == "Zero")
{
value = Simple_Zero_Custom;
}
else if (str == "One")
{
value = Simple_One_Custom;
}
else if (str == "Two")
{
value = Simple_Two_Custom;
}
else
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Can't convert '%s' to SimpleEnumCustom.", str.c_str());
}
});

constructor.RegisterScalar<ScopedEnumCustom>(
[](const ScopedEnumCustom& value, Rml::Variant& variant) {
if (value == ScopedEnumCustom::Zero)
{
variant = "Zero";
}
else if (value == ScopedEnumCustom::One)
{
variant = "One";
}
else if (value == ScopedEnumCustom::Two)
{
variant = "Two";
}
else
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Invalid value for ScopedEnumCustom type.");
}
},
[](ScopedEnumCustom& value, const Rml::Variant& variant) {
Rml::String str = variant.Get<Rml::String>();
if (str == "Zero")
{
value = ScopedEnumCustom::Zero;
}
else if (str == "One")
{
value = ScopedEnumCustom::One;
}
else if (str == "Two")
{
value = ScopedEnumCustom::Two;
}
else
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Can't convert '%s' to ScopedEnumCustom.", str.c_str());
}
});

{
// Globals
constructor.Bind("i0", &globals.i0);
Expand All @@ -386,7 +472,9 @@ bool InitializeDataBindings(Context* context)
constructor.Bind("s5", &globals.s5);

constructor.Bind("simple", &globals.simple);
constructor.Bind("simple_custom", &globals.simple_custom);
constructor.Bind("scoped", &globals.scoped);
constructor.Bind("scoped_custom", &globals.scoped_custom);
// Invalid: Each of the following should give a compile-time failure.
// constructor.Bind("x0", &globals.x0);
// constructor.Bind("x1", &globals.x1);
Expand Down Expand Up @@ -482,6 +570,18 @@ TEST_CASE("data_binding")

TestsShell::RenderLoop();

Element* element = document->GetElementById("simple");
CHECK(element->GetInnerRML() == "1");

element = document->GetElementById("simple_custom");
CHECK(element->GetInnerRML() == "One");

element = document->GetElementById("scoped");
CHECK(element->GetInnerRML() == "1");

element = document->GetElementById("scoped_custom");
CHECK(element->GetInnerRML() == "One");

document->Close();

TestsShell::ShutdownShell();
Expand Down

0 comments on commit 02494ca

Please sign in to comment.