/* * 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 "ElementDataModels.h" #include "../../Include/RmlUi/Core/Context.h" #include "../../Include/RmlUi/Core/Core.h" #include "../../Include/RmlUi/Core/DataModelHandle.h" #include "../../Include/RmlUi/Core/DataVariable.h" #include "../../Include/RmlUi/Core/StyleSheet.h" #include "../../Include/RmlUi/Core/StyleSheetSpecification.h" #include "../../Include/RmlUi/Core/SystemInterface.h" #include "CommonSource.h" #include "DataModelsSource.h" #include namespace Rml { namespace Debugger { static void ReadDataVariableRecursive(String& out_rml, const int indent_level, const DataVariable variable) { constexpr int max_num_child_elements = 50; String indent_child_element; for (int i = 0; i <= indent_level; i++) indent_child_element += "  "; switch (variable.Type()) { case DataVariableType::Scalar: { Variant variant; if (!variable.Get(variant)) out_rml += "(unreadable)"; else if (variant.GetType() == Variant::NONE) out_rml += "(none)"; else if (variant.GetType() == Variant::BOOL) out_rml += variant.Get() ? "true" : "false"; else out_rml += StringUtilities::EncodeRml(variant.Get()); out_rml += "
"; } break; case DataVariableType::Array: { const int size = variable.Size(); out_rml += "Array (" + ToString(variable.Size()) + ")
"; for (int i = 0; i < size; i++) { if (i >= max_num_child_elements) { out_rml += indent_child_element + "[...]
"; break; } out_rml += indent_child_element + "[" + ToString(i) + "]: "; DataVariable child_variable = variable.Child(DataAddressEntry(i)); ReadDataVariableRecursive(out_rml, indent_level + 1, child_variable); } } break; case DataVariableType::Struct: { const StringList members = Detail::DataVariableAccessor::GetDefinition(variable)->ReflectMemberNames(); out_rml += "Struct (" + ToString(members.size()) + ")
"; for (const String& member_name : members) { if (std::distance(&members.front(), &member_name) >= max_num_child_elements) { out_rml += indent_child_element + "[...]
"; break; } out_rml += indent_child_element + "." + member_name + ": "; DataVariable child_variable = variable.Child(DataAddressEntry(member_name)); ReadDataVariableRecursive(out_rml, indent_level + 1, child_variable); } } break; } } ElementDataModels::ElementDataModels(const String& tag) : ElementDebugDocument(tag) {} ElementDataModels::~ElementDataModels() { RemoveEventListener(EventId::Click, this); } bool ElementDataModels::Initialise(Context* target_context) { SetInnerRML(data_models_rml); SetId("rmlui-debug-data-models"); AddEventListener(EventId::Click, this); SharedPtr style_sheet = Factory::InstanceStyleSheetString(String(common_rcss) + String(data_models_rcss)); if (!style_sheet) return false; SetStyleSheetContainer(std::move(style_sheet)); SetDebugContext(target_context); return true; } void ElementDataModels::Reset() { SetDebugContext(nullptr); } void ElementDataModels::SetDebugContext(Context* new_debug_context) { debug_context = new_debug_context; } void ElementDataModels::OnUpdate() { if (!IsVisible() || !debug_context) return; const double t = GetSystemInterface()->GetElapsedTime(); const float dt = (float)(t - previous_update_time); constexpr float update_interval = 0.3f; if (dt > update_interval) { previous_update_time = t; UpdateContent(); } } void ElementDataModels::ProcessEvent(Event& event) { if (!IsVisible()) return; Element* target_element = event.GetTargetElement(); if (target_element->GetOwnerDocument() != this) return; if (event == EventId::Click) { const String& id = event.GetTargetElement()->GetId(); if (id == "close_button") Hide(); event.StopPropagation(); } } void ElementDataModels::UpdateContent() { RMLUI_ASSERT(debug_context); Element* models_content_element = GetElementById("content"); SmallOrderedMap new_model_rml_map; const UnorderedMap data_models = debug_context->GetDataModels(); for (const auto& name_model_pair : data_models) { const String& model_name = name_model_pair.first; const DataModelConstructor& model_constructor = name_model_pair.second; String& model_rml = new_model_rml_map[model_name]; model_rml += "
"; model_rml += "

" + model_name + "

"; model_rml += "
"; const UnorderedMap& variables = Detail::DataModelConstructorAccessor::GetAllVariables(model_constructor); for (const auto& name_variable_pair : variables) { const String& name = name_variable_pair.first; const DataVariable& variable = name_variable_pair.second; model_rml += "" + name + ": "; ReadDataVariableRecursive(model_rml, 0, variable); } if (variables.empty()) model_rml += "No data variables in data model.
"; model_rml += "
"; model_rml += "
"; } if (new_model_rml_map != model_rml_map) { model_rml_map = std::move(new_model_rml_map); String models_rml; for (const auto& name_rml_pair : model_rml_map) models_rml += name_rml_pair.second; if (data_models.empty()) models_rml = "No data models in context."; models_content_element->SetInnerRML(models_rml); } } } // namespace Debugger } // namespace Rml