/* * This source file is part of RmlUi, the HTML/CSS Interface Middleware * * For the latest information, see http://github.com/mikke89/RmlUi * * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd * Copyright (c) 2019-2023 The RmlUi Team, and contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include "../Common/TestsShell.h" #include #include #include #include #include #include using namespace Rml; using namespace ankerl; static const String document_rml = R"( Test

Globals

0

{{ i0 }}

{{ i1 }}

{{ i2 }}

{{ i3 }}

Basic

{{ basic.a }}

{{ basic.b }}

{{ basic.c.val }}

Arrays

10 20 30

{{ it }}

{{ it }}

{{ it.val }}

{{ 'a: ' + it.a + ', b: ' + it.b + ', c: ' + it.c.val + ' :: ' }}

)"; struct StringWrap { StringWrap(String val = "wrap_default") : val(val) {} String val; }; struct Globals { int i0 = 0; int* i1 = new int(1); UniquePtr i2 = MakeUnique(2); SharedPtr i3 = MakeShared(3); } globals; struct Basic { int a = 1; int* b = new int(2); StringWrap* c = new StringWrap("basic.c"); }; struct Arrays { Vector a = {10, 11, 12}; Vector b = {new int(20), new int(21), new int(22)}; Vector c = {StringWrap("c1"), StringWrap("c2"), StringWrap("c3")}; Vector d = {Basic{10}, Basic{20}, Basic{30}}; }; static UniquePtr basic; static UniquePtr arrays; static DataModelHandle InitializeDataBindings(Context* context) { Rml::DataModelConstructor constructor = context->CreateDataModel("basics"); if (!constructor) return DataModelHandle(); if (auto handle = constructor.RegisterStruct()) handle.RegisterMember("val", &StringWrap::val); constructor.Bind("i0", &globals.i0); constructor.Bind("i1", &globals.i1); constructor.Bind("i2", &globals.i2); constructor.Bind("i3", &globals.i3); if (auto handle = constructor.RegisterStruct()) { handle.RegisterMember("a", &Basic::a); handle.RegisterMember("b", &Basic::b); handle.RegisterMember("c", &Basic::c); } basic = MakeUnique(); constructor.Bind("basic", basic.get()); constructor.RegisterArray(); constructor.RegisterArray(); constructor.RegisterArray(); constructor.RegisterArray(); if (auto handle = constructor.RegisterStruct()) { handle.RegisterMember("a", &Arrays::a); handle.RegisterMember("b", &Arrays::b); handle.RegisterMember("c", &Arrays::c); handle.RegisterMember("d", &Arrays::d); } arrays = MakeUnique(); constructor.Bind("arrays", arrays.get()); DataModelHandle model_handle = constructor.GetModelHandle(); return model_handle; } TEST_CASE("data_binding") { Context* context = TestsShell::GetContext(); REQUIRE(context); DataModelHandle model_handle = InitializeDataBindings(context); model_handle.DirtyAllVariables(); ElementDocument* document = context->LoadDocumentFromMemory(document_rml); REQUIRE(document); document->Show(); context->Update(); context->Render(); SUBCASE("dirty") { nanobench::Bench bench; bench.title("Data bindings: Dirty variables"); bench.relative(true); bench.run("Reference (Update)", [&] { context->Update(); }); bench.run("Dirty one variable", [&] { model_handle.DirtyVariable("i0"); context->Update(); }); bench.run("Dirty big variable", [&] { model_handle.DirtyVariable("arrays"); context->Update(); }); bench.run("Dirty all variables", [&] { model_handle.DirtyAllVariables(); context->Update(); }); } SUBCASE("update") { Element* element_i = document->GetElementById("i"); Element* element_array = document->GetElementById("array"); nanobench::Rng rng; nanobench::Bench bench; bench.title("Data bindings: Update"); bench.relative(true); bench.run("Reference (Integer)", [&] { element_i->SetInnerRML(Rml::ToString(rng.bounded(1000))); context->Update(); }); bench.run("Integer", [&] { globals.i0 = rng.bounded(1000); model_handle.DirtyVariable("i0"); context->Update(); }); bench.run("Basic", [&] { basic->a = rng.bounded(2000); *basic->b = rng.bounded(3000); basic->c->val = String("abc") + String(5, char('a' + rng.bounded('z' - 'a'))); model_handle.DirtyVariable("basic"); context->Update(); }); bench.run("Reference (Arrays)", [&] { element_array->SetInnerRML( Rml::CreateString("%d %d %d ", rng.bounded(5000), rng.bounded(5000), rng.bounded(5000))); context->Update(); }); bench.run("Arrays", [&] { for (auto& v : arrays->a) v = rng.bounded(5000); model_handle.DirtyVariable("arrays"); context->Update(); }); } TestsShell::RenderLoop(); document->Close(); TestsShell::ShutdownShell(); }