diff --git a/Include/RmlUi/Core/DataModelHandle.h b/Include/RmlUi/Core/DataModelHandle.h index 3829d70e0..12641e566 100644 --- a/Include/RmlUi/Core/DataModelHandle.h +++ b/Include/RmlUi/Core/DataModelHandle.h @@ -143,7 +143,8 @@ class RMLUICORE_API DataModelConstructor { template inline bool DataModelConstructor::RegisterScalar(DataTypeGetFunc get_func, DataTypeSetFunc set_func) { - static_assert(!is_builtin_data_scalar::value, + // Though enum is builtin scalar type, we allow custom getter/setter on it. + static_assert(!is_builtin_data_scalar::value || std::is_enum::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::Id(); diff --git a/Tests/Source/UnitTests/DataBinding.cpp b/Tests/Source/UnitTests/DataBinding.cpp index 3aa8ee976..aca93eeed 100644 --- a/Tests/Source/UnitTests/DataBinding.cpp +++ b/Tests/Source/UnitTests/DataBinding.cpp @@ -79,8 +79,10 @@ static const String document_rml = R"(

{{ s3.val }}

{{ s4.val }}

{{ s5.val }}

-

{{ simple }}

-

{{ scoped }}

+

{{ simple }}

+

{{ simple_custom }}

+

{{ scoped }}

+

{{ scoped_custom }}

Basic

{{ basic.a }}

@@ -210,8 +212,12 @@ 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); @@ -219,7 +225,9 @@ struct Globals { SharedPtr i3 = MakeShared(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"); @@ -371,6 +379,84 @@ bool InitializeDataBindings(Context* context) handle.RegisterMember("val", &StringWrap::val); } + constructor.RegisterScalar( + [](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(); + 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( + [](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(); + 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); @@ -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); @@ -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();