/* * 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 #include using namespace Rml; static const String basic_doc_rml = R"( Test )"; static const String data_model_doc_rml_pre = R"( Test
)"; static const String data_model_doc_rml_post = R"(
)"; static String GetSelectValueRml(ElementFormControlSelect* select_element) { for (int child_index = 0; child_index < select_element->GetNumChildren(true); child_index++) { Element* child = select_element->GetChild(child_index); if (child->GetTagName() == "selectvalue") return child->GetInnerRML(); } return String(); } TEST_CASE("form.select.value") { Context* context = TestsShell::GetContext(); REQUIRE(context); struct Test { const String rml; const String expected_value; const String expected_value_rml; }; Test tests[] = { { R"( )", "", "A"}, { R"( )", "a", "A"}, { R"( )", "", "A"}, { R"( )", "c", "C"}, { R"( )", "d", "D"}, { R"( )", "a", "A"}, { R"( )", "d", "D"}, { R"( )", "c", "C"}, }; ElementDocument* document = context->LoadDocumentFromMemory(basic_doc_rml); REQUIRE(document); document->Show(); context->Update(); context->Render(); int i = 0; for (const Test& test : tests) { document->SetInnerRML(test.rml); context->Update(); REQUIRE(document->GetNumChildren() == 1); ElementFormControlSelect* select_element = rmlui_dynamic_cast(document->GetFirstChild()); REQUIRE(select_element); const String value = select_element->GetValue(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK_MESSAGE(value == test.expected_value, "Document " << i); CHECK_MESSAGE(selectvalue_rml == test.expected_value_rml, "Document " << i); i++; } document->Close(); TestsShell::ShutdownShell(); } TEST_CASE("form.select.data_binding") { Context* context = TestsShell::GetContext(); REQUIRE(context); struct Test { const String rml; const String expected_value; const String expected_value_rml; }; int selected_index = 2; String selected_value = "b"; Vector values = {"a", "b", "c", "d"}; Test tests[] = { { R"( )", "2", "C"}, { R"( )", "b", "B"}, { R"( )", "b", "B"}, { R"( )", "b", "B"}, { R"( )", "2", "C"}, { R"( )", "b", "B"}, { R"( )", "2", "C"}, { R"( )", "2", "

C

"}, }; DataModelConstructor constructor = context->CreateDataModel("select-test"); constructor.RegisterArray>(); constructor.Bind("selected_index", &selected_index); constructor.Bind("selected_value", &selected_value); constructor.Bind("subjects", &values); int i = 0; for (const Test& test : tests) { const String document_rml = data_model_doc_rml_pre + test.rml + data_model_doc_rml_post; ElementDocument* document = context->LoadDocumentFromMemory(document_rml); REQUIRE(document); document->Show(); TestsShell::RenderLoop(); Element* wrapper_element = document->GetElementById("select_wrap"); REQUIRE(wrapper_element); REQUIRE(wrapper_element->GetNumChildren() == 1); ElementFormControlSelect* select_element = rmlui_dynamic_cast(wrapper_element->GetFirstChild()); REQUIRE(select_element); const String value = select_element->GetValue(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK_MESSAGE(value == test.expected_value, "Document " << i); CHECK_MESSAGE(selectvalue_rml == test.expected_value_rml, "Document " << i); i++; document->Close(); } TestsShell::ShutdownShell(); } TEST_CASE("form.select.data-for") { Context* context = TestsShell::GetContext(); REQUIRE(context); struct Test { const String rml; const String expected_value; const String expected_value_rml; }; String selected_value = "b"; Vector values = {"a", "b", "c", "d"}; DataModelConstructor constructor = context->CreateDataModel("select-test"); constructor.RegisterArray>(); constructor.Bind("selected_value", &selected_value); constructor.Bind("subjects", &values); DataModelHandle handle = constructor.GetModelHandle(); { const String select_rml = R"( )"; const String document_rml = data_model_doc_rml_pre + select_rml + data_model_doc_rml_post; ElementDocument* document = context->LoadDocumentFromMemory(document_rml); REQUIRE(document); document->Show(); Element* wrapper_element = document->GetElementById("select_wrap"); REQUIRE(wrapper_element); REQUIRE(wrapper_element->GetNumChildren() == 1); ElementFormControlSelect* select_element = rmlui_dynamic_cast(wrapper_element->GetFirstChild()); REQUIRE(select_element); { // TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(value == "b"); CHECK(selected_index == 1); CHECK(selectvalue_rml == "B"); } { selected_value = "d"; handle.DirtyVariable("selected_value"); context->Update(); // TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(value == "d"); CHECK(selected_index == 3); CHECK(selectvalue_rml == "D"); } { select_element->SetValue("a"); context->Update(); TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(selected_value == "a"); CHECK(value == "a"); CHECK(selected_index == 0); CHECK(selectvalue_rml == "A"); } { select_element->SetSelection(1); context->Update(); TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(selected_value == "b"); CHECK(value == "b"); CHECK(selected_index == 1); CHECK(selectvalue_rml == "B"); } #if 0 // These are not supported now, we may want to add support later. { // Values: a c b d std::swap(values[1], values[2]); handle.DirtyVariable("subjects"); context->Update(); TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(value == "b"); CHECK(selected_index == 2); CHECK(selectvalue_rml == "B"); } { // Values: c b d values.erase(values.begin()); handle.DirtyVariable("subjects"); context->Update(); TestsShell::RenderLoop(); const String value = select_element->GetValue(); const int selected_index = select_element->GetSelection(); const String selectvalue_rml = GetSelectValueRml(select_element); CHECK(value == "b"); CHECK(selected_index == 1); CHECK(selectvalue_rml == "B"); } #endif document->Close(); } TestsShell::ShutdownShell(); } static const String event_doc_rml = R"( )"; static void ClickAt(Context* context, int x, int y) { context->Update(); context->Render(); context->ProcessMouseMove(x, y, 0); context->Update(); context->Render(); context->ProcessMouseButtonDown(0, 0); context->ProcessMouseButtonUp(0, 0); context->Update(); context->Render(); } TEST_CASE("form.select.event.change") { Context* context = TestsShell::GetContext(); REQUIRE(context); ElementDocument* document = context->LoadDocumentFromMemory(event_doc_rml); REQUIRE(document); document->Show(); struct SelectionEventListener : public Rml::EventListener { void ProcessEvent(Rml::Event& ev) override { num_events_processed += 1; value = ev.GetParameter("value", "*empty*"); } int num_events_processed = 0; String value; }; auto listener = MakeUnique(); document->GetElementById("sel")->AddEventListener(Rml::EventId::Change, listener.get()); ClickAt(context, 100, 20); // Open select ClickAt(context, 100, 40); // Click option 'Cube' CHECK(listener->num_events_processed == 0); ClickAt(context, 100, 20); // Open select ClickAt(context, 100, 63); // Click option 'Cone' CHECK(listener->num_events_processed == 1); CHECK(listener->value == ""); ClickAt(context, 100, 20); // Open select again ClickAt(context, 100, 85); // Click option 'Cylinder' CHECK(listener->num_events_processed == 2); CHECK(listener->value == "c"); ClickAt(context, 100, 20); // Open select again context->ProcessMouseMove(100, 85, 0); // Hover option 'Cylinder' context->Update(); context->Render(); context->ProcessKeyDown(Rml::Input::KI_DOWN, 0); context->Update(); context->Render(); CHECK(listener->num_events_processed == 3); CHECK(listener->value == "d"); context->ProcessKeyDown(Rml::Input::KI_DOWN, 0); context->Update(); context->Render(); CHECK(listener->num_events_processed == 3); document->Close(); TestsShell::ShutdownShell(); }