瀏覽代碼

Move creation of data views and controllers to ElementUtilities. Clean up.

Michael Ragazzon 5 年之前
父節點
當前提交
16413ecc8b

+ 8 - 13
Include/RmlUi/Core/Context.h

@@ -221,18 +221,12 @@ public:
 	/// @param[in] instancer The context's instancer.
 	void SetInstancer(ContextInstancer* instancer);
 
-
+	/// Creates a data model.
+	/// Elements can bind to the model using the attribute 'data-model="name"'.
+	/// @param[in] name The name of the data model.
+	/// @return A handle to the data model which can be used to bind data variables.
 	DataModelHandle CreateDataModel(const String& name);
 
-	// Todo: Remove
-	DataModel* GetDataModel(const String& name)
-	{
-		auto it = data_models.find(name);
-		if (it != data_models.end())
-			return it->second.get();
-		return nullptr;
-	}
-
 protected:
 	void Release() override;
 
@@ -317,13 +311,14 @@ private:
 	// Updates the current hover elements, sending required events.
 	void UpdateHoverChain(const Dictionary& parameters, const Dictionary& drag_parameters, const Vector2i& old_mouse_position);
 
-	// Creates the drag clone from the given element. The old drag clone will be released if
-	// necessary.
-	// @param[in] element The element to clone.
+	// Creates the drag clone from the given element. The old drag clone will be released if necessary.
 	void CreateDragClone(Element* element);
 	// Releases the drag clone, if one exists.
 	void ReleaseDragClone();
 
+	// Returns the data model with the provided name, or nullptr if it does not exist.
+	DataModel* GetDataModel(const String& name) const;
+
 	// Builds the parameters for a generic key event.
 	void GenerateKeyEventParameters(Dictionary& parameters, Input::KeyIdentifier key_identifier);
 	// Builds the parameters for a generic mouse event.

+ 3 - 3
Include/RmlUi/Core/DataView.h

@@ -98,7 +98,7 @@ private:
 
 class DataViewAttribute final : public DataView {
 public:
-	DataViewAttribute(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name);
+	DataViewAttribute(DataModel& model, Element* element, const String& binding_name, const String& attribute_name);
 
 	bool Update(DataModel& model) override;
 
@@ -113,7 +113,7 @@ private:
 
 class DataViewStyle final : public DataView {
 public:
-	DataViewStyle(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name);
+	DataViewStyle(DataModel& model, Element* element, const String& binding_name, const String& property_name);
 
 	bool Update(DataModel& model) override;
 
@@ -128,7 +128,7 @@ private:
 
 class DataViewIf final : public DataView {
 public:
-	DataViewIf(DataModel& model, Element* element, Element* parent, const String& binding_name);
+	DataViewIf(DataModel& model, Element* element, const String& binding_name);
 
 	bool Update(DataModel& model) override;
 

+ 4 - 14
Include/RmlUi/Core/Element.h

@@ -56,7 +56,6 @@ class ElementDefinition;
 class ElementDocument;
 class ElementScroll;
 class ElementStyle;
-class Factory;
 class PropertiesIteratorView;
 class FontFaceHandleDefault;
 class PropertyDictionary;
@@ -535,23 +534,19 @@ public:
 	 */
 	//@{
 	/// Access the event dispatcher for this element.
-	/// @return The element's dispatcher.
 	EventDispatcher* GetEventDispatcher() const;
 	/// Returns event types with number of listeners for debugging.
-	/// @return Summary of attached listeners.
 	String GetEventDispatcherSummary() const;
 	/// Access the element background.
-	/// @return The element's background.
 	ElementBackground* GetElementBackground() const;
 	/// Access the element border.
-	/// @return The element's boder.
 	ElementBorder* GetElementBorder() const;
 	/// Access the element decorators.
-	/// @return The element decoration.
 	ElementDecoration* GetElementDecoration() const;
 	/// Returns the element's scrollbar functionality.
-	/// @return The element's scrolling functionality.
 	ElementScroll* GetElementScroll() const;
+	/// Returns the data model of this element.
+	DataModel* GetDataModel() const;
 	//@}
 	
 	/// Returns true if this element requires clipping
@@ -575,12 +570,6 @@ public:
 	/// Return the computed values of the element's properties. These values are updated as appropriate on every Context::Update.
 	const ComputedValues& GetComputedValues() const;
 
-
-
-	// TODO: Temporary, make private / remove.
-	DataModel* GetDataModel() { return data_model; }
-	void SetDataModel(DataModel* new_data_model) { data_model = new_data_model; }
-
 protected:
 	void Update(float dp_ratio);
 	void Render();
@@ -630,6 +619,8 @@ protected:
 
 private:
 	void SetParent(Element* parent);
+	
+	void SetDataModel(DataModel* new_data_model);
 
 	void DirtyOffset();
 	void UpdateOffset();
@@ -748,7 +739,6 @@ private:
 	friend class LayoutInlineBox;
 	friend struct ElementDeleter;
 	friend class ElementScroll;
-	friend class Factory;
 };
 
 }

+ 5 - 1
Include/RmlUi/Core/ElementUtilities.h

@@ -131,7 +131,11 @@ public:
 	/// Note: All calls to RenderInterface::SetTransform must go through here.
 	/// @param[in] element		The element whose transform to apply.
 	/// @return true if a render interface is available to set the transform.
-	static bool ApplyTransform(Element &element);
+	static bool ApplyTransform(Element& element);
+
+	/// Creates data views and data controllers if a data model applies to the element.
+	/// Attributes such as 'data-' are used to create the views and controllers.
+	static void ApplyDataViewsControllers(Element* element);
 };
 
 }

+ 10 - 0
Source/Core/Context.cpp

@@ -1156,6 +1156,16 @@ void Context::ReleaseDragClone()
 	}
 }
 
+// Returns the data model with the provided name, or nullptr if it does not exist.
+
+DataModel* Context::GetDataModel(const String& name) const
+{
+	auto it = data_models.find(name);
+	if (it != data_models.end())
+		return it->second.get();
+	return nullptr;
+}
+
 // Builds the parameters for a generic key event.
 void Context::GenerateKeyEventParameters(Dictionary& parameters, Input::KeyIdentifier key_identifier)
 {

+ 9 - 9
Source/Core/DataView.cpp

@@ -165,10 +165,10 @@ String DataViewText::BuildText() const
 }
 
 
-DataViewAttribute::DataViewAttribute(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& attribute_name)
+DataViewAttribute::DataViewAttribute(DataModel& model, Element* element, const String& binding_name, const String& attribute_name)
 	: DataView(element), attribute_name(attribute_name)
 {
-	variable_address = model.ResolveAddress(binding_name, parent);
+	variable_address = model.ResolveAddress(binding_name, element);
 
 	if (attribute_name.empty())
 		InvalidateView();
@@ -197,10 +197,10 @@ bool DataViewAttribute::Update(DataModel& model)
 
 
 
-DataViewStyle::DataViewStyle(DataModel& model, Element* element, Element* parent, const String& binding_name, const String& property_name)
+DataViewStyle::DataViewStyle(DataModel& model, Element* element, const String& binding_name, const String& property_name)
 	: DataView(element), property_name(property_name)
 {
-	variable_address = model.ResolveAddress(binding_name, parent);
+	variable_address = model.ResolveAddress(binding_name, element);
 	
 	if (variable_address.empty() || property_name.empty())
 		InvalidateView();
@@ -230,7 +230,7 @@ bool DataViewStyle::Update(DataModel& model)
 
 
 
-DataViewIf::DataViewIf(DataModel& model, Element* element, Element* parent, const String& binding_name) : DataView(element)
+DataViewIf::DataViewIf(DataModel& model, Element* element, const String& binding_name) : DataView(element)
 {
 	variable_address = model.ResolveAddress(binding_name, element);
 	if (variable_address.empty())
@@ -314,16 +314,16 @@ bool DataViewFor::Update(DataModel& model)
 		if (i >= num_elements)
 		{
 			ElementPtr new_element_ptr = Factory::InstanceElement(nullptr, element->GetTagName(), element->GetTagName(), attributes);
-			new_element_ptr->SetDataModel((DataModel*)(&model));
-			Element* new_element = element->GetParentNode()->InsertBefore(std::move(new_element_ptr), element);
-			elements.push_back(new_element);
 
 			Address replacement_address;
 			replacement_address.reserve(variable_address.size() + 1);
 			replacement_address = variable_address;
 			replacement_address.push_back(AddressEntry(i));
 
-			model.InsertAlias(new_element, alias_name, replacement_address);
+			model.InsertAlias(new_element_ptr.get(), alias_name, replacement_address);
+
+			Element* new_element = element->GetParentNode()->InsertBefore(std::move(new_element_ptr), element);
+			elements.push_back(new_element);
 
 			elements[i]->SetInnerRML(rml_contents);
 

+ 48 - 12
Source/Core/Element.cpp

@@ -1510,6 +1510,11 @@ ElementScroll* Element::GetElementScroll() const
 {
 	return &meta->scroll;
 }
+
+DataModel* Element::GetDataModel() const
+{
+	return data_model;
+}
 	
 int Element::GetClippingIgnoreDepth()
 {
@@ -1603,7 +1608,6 @@ void Element::OnAttributeChange(const ElementAttributes& changed_attributes)
 		meta->style.SetClassNames(it->second.Get<String>());
 	}
 
-	// Add any inline style declarations.
 	it = changed_attributes.find("style");
 	if (it != changed_attributes.end())
 	{
@@ -1611,11 +1615,8 @@ void Element::OnAttributeChange(const ElementAttributes& changed_attributes)
 		StyleSheetParser parser;
 		parser.ParseProperties(properties, it->second.Get<String>());
 
-		Rml::Core::PropertyMap property_map = properties.GetProperties();
-		for (Rml::Core::PropertyMap::iterator i = property_map.begin(); i != property_map.end(); ++i)
-		{
-			meta->style.SetProperty((*i).first, (*i).second);
-		}
+		for (auto& id_property_pair : properties.GetProperties())
+			meta->style.SetProperty(id_property_pair.first, id_property_pair.second);
 	}
 }
 
@@ -1913,12 +1914,6 @@ void Element::SetOwnerDocument(ElementDocument* document)
 			// We are detaching from the document and thereby also the context.
 			if (Context * context = owner_document->GetContext())
 				context->OnElementDetach(this);
-
-			if (data_model)
-			{
-				data_model->OnElementRemove(this);
-				data_model = nullptr;
-			}
 		}
 
 		if (owner_document != document)
@@ -1930,6 +1925,25 @@ void Element::SetOwnerDocument(ElementDocument* document)
 	}
 }
 
+void Element::SetDataModel(DataModel* new_data_model) 
+{
+	RMLUI_ASSERTMSG(!data_model || !new_data_model, "We must either attach a new data model, or detach the old one.");
+
+	if (data_model == new_data_model)
+		return;
+
+	if (data_model)
+		data_model->OnElementRemove(this);
+
+	data_model = new_data_model;
+
+	if (data_model)
+		ElementUtilities::ApplyDataViewsControllers(this);
+
+	for (ElementPtr& child : children)
+		child->SetDataModel(new_data_model);
+}
+
 void Element::Release()
 {
 	if (instancer)
@@ -1957,6 +1971,28 @@ void Element::SetParent(Element* _parent)
 		DirtyTransformState(true, true);
 
 	SetOwnerDocument(parent ? parent->GetOwnerDocument() : nullptr);
+
+	if (!parent)
+	{
+		if (data_model)
+			SetDataModel(nullptr);
+	}
+	else 
+	{
+		auto it = attributes.find("data-model");
+		if (it == attributes.end())
+		{
+			SetDataModel(parent->data_model);
+		}
+		else if (Context* context = GetContext())
+		{
+			String name = it->second.Get<String>();
+			if (DataModel* model = context->GetDataModel(name))
+				SetDataModel(model);
+			else
+				Log::Message(Log::LT_WARNING, "Could not locate data model '%s'.", name.c_str());
+		}
+	}
 }
 
 void Element::DirtyOffset()

+ 82 - 0
Source/Core/ElementUtilities.cpp

@@ -381,5 +381,87 @@ bool ElementUtilities::ApplyTransform(Element &element)
 	return true;
 }
 
+void ElementUtilities::ApplyDataViewsControllers(Element* element)
+{
+	RMLUI_ASSERT(element);
+
+	// If we have an active data model, check the attributes for any data bindings
+	if (DataModel* data_model = element->GetDataModel())
+	{
+		for (auto& attribute : element->GetAttributes())
+		{
+			auto& name = attribute.first;
+
+			if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
+			{
+				const size_t data_type_end = name.find('-', 5);
+				const size_t count = (data_type_end == String::npos ? String::npos : data_type_end - 5);
+				const String data_type = name.substr(5, count);
+				const String value_bind_name = attribute.second.Get<String>();
+
+				if (data_type == "attr")
+				{
+					const String attr_bind_name = name.substr(5 + data_type.size() + 1);
+
+					auto view = std::make_unique<DataViewAttribute>(*data_model, element, value_bind_name, attr_bind_name);
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-attr view to element '%s'.", element->GetAddress().c_str());
+				}
+				else if (data_type == "value")
+				{
+					const String attr_bind_name = "value";
+					auto view = std::make_unique<DataViewAttribute>(*data_model, element, value_bind_name, attr_bind_name);
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-value view to element '%s'.", element->GetAddress().c_str());
+
+					auto controller = std::make_unique<DataControllerValue>(*data_model, element, value_bind_name);
+					if (controller)
+						data_model->AddController(std::move(controller));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-value controller to element '%s'.", element->GetAddress().c_str());
+				}
+				else if (data_type == "style")
+				{
+					const String property_name = name.substr(5 + data_type.size() + 1);
+
+					auto view = std::make_unique<DataViewStyle>(*data_model, element, value_bind_name, property_name);
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-style view to element '%s'.", element->GetAddress().c_str());
+				}
+				else if (data_type == "if")
+				{
+					auto view = std::make_unique<DataViewIf>(*data_model, element, value_bind_name);
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-if view to element '%s'.", element->GetAddress().c_str());
+				}
+			}
+		}
+
+		if (ElementText* element_text = rmlui_dynamic_cast<ElementText*>(element))
+		{
+			const String& text = element_text->GetText();
+
+			// Scan text for {{ data bindings }}
+			const size_t i_brackets = text.find("{{", 0);
+			if (i_brackets != String::npos)
+			{
+				auto view = std::make_unique<DataViewText>(*data_model, element_text, text, i_brackets);
+				if (*view)
+					data_model->AddView(std::move(view));
+				else
+					Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", element->GetAddress().c_str());
+			}
+		}
+	}
+}
+
 }
 }

+ 7 - 118
Source/Core/Factory.cpp

@@ -239,96 +239,6 @@ ElementPtr Factory::InstanceElement(Element* parent, const String& instancer_nam
 			element->SetAttributes(attributes);
 			ElementUtilities::BindEventAttributes(element.get());
 
-
-			// TODO: Relies on parent, a bit hacky.
-			if (parent && !parent->HasAttribute("data-for"))
-			{
-				// Look for the data-model attribute or otherwise copy it from the parent element
-				auto it = attributes.find("data-model");
-				if (it != attributes.end())
-				{
-					String error_msg;
-
-					if (auto context = parent->GetContext())
-					{
-						String name = it->second.Get<String>();
-						if (auto model = context->GetDataModel(name))
-							element->data_model = model;
-						else
-							Log::Message(Log::LT_WARNING, "Could not locate data model '%s'.", name.c_str());
-					}
-					else
-					{
-						Log::Message(Log::LT_WARNING, "Could not add data model to element '%s' because the context is not available.", element->GetAddress().c_str());
-					}
-				}
-				else if (parent && parent->data_model)
-				{
-					element->data_model = parent->data_model;
-				}
-
-				// If we have an active data model, check the attributes for any data bindings
-				if (DataModel* data_model = element->data_model)
-				{
-					for (auto& attribute : attributes)
-					{
-						auto& name = attribute.first;
-
-						if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
-						{
-							const size_t data_type_end = name.find('-', 5);
-							const size_t count = (data_type_end == String::npos ? String::npos : data_type_end - 5);
-							const String data_type = name.substr(5, count);
-							const String value_bind_name = attribute.second.Get<String>();
-
-							if (data_type == "attr")
-							{
-								const String attr_bind_name = name.substr(5 + data_type.size() + 1);
-
-								auto view = std::make_unique<DataViewAttribute>(*data_model, element.get(), parent, value_bind_name, attr_bind_name);
-								if (*view)
-									data_model->AddView(std::move(view));
-								else
-									Log::Message(Log::LT_WARNING, "Could not add data-attr view to element '%s'.", parent->GetAddress().c_str());
-							}
-							else if (data_type == "value")
-							{
-								const String attr_bind_name = "value";
-								auto view = std::make_unique<DataViewAttribute>(*data_model, element.get(), parent, value_bind_name, attr_bind_name);
-								if (*view)
-									data_model->AddView(std::move(view));
-								else
-									Log::Message(Log::LT_WARNING, "Could not add data-value view to element '%s'.", parent->GetAddress().c_str());
-
-								auto controller = std::make_unique<DataControllerValue>(*data_model, element.get(), value_bind_name);
-								if (controller)
-									data_model->AddController(std::move(controller));
-								else
-									Log::Message(Log::LT_WARNING, "Could not add data-value controller to element '%s'.", parent->GetAddress().c_str());
-							}
-							else if (data_type == "style")
-							{
-								const String property_name = name.substr(5 + data_type.size() + 1);
-
-								auto view = std::make_unique<DataViewStyle>(*data_model, element.get(), parent, value_bind_name, property_name);
-								if (*view)
-									data_model->AddView(std::move(view));
-								else
-									Log::Message(Log::LT_WARNING, "Could not add data-style view to element '%s'.", parent->GetAddress().c_str());
-							}
-							else if (data_type == "if")
-							{
-								auto view = std::make_unique<DataViewIf>(*data_model, element.get(), parent, value_bind_name);
-								if (*view)
-									data_model->AddView(std::move(view));
-								else
-									Log::Message(Log::LT_WARNING, "Could not add data-if view to element '%s'.", parent->GetAddress().c_str());
-							}
-						}
-					}
-				}
-			}
-
 			PluginRegistry::NotifyElementCreate(element.get());
 		}
 
@@ -378,46 +288,25 @@ bool Factory::InstanceElementText(Element* parent, const String& text)
 
 		// Attempt to instance the element.
 		XMLAttributes attributes;
-		ElementPtr element_ptr = Factory::InstanceElement(parent, "#text", "#text", attributes);
-		if (!element_ptr)
+		ElementPtr element = Factory::InstanceElement(parent, "#text", "#text", attributes);
+		if (!element)
 		{
 			Log::Message(Log::LT_ERROR, "Failed to instance text element '%s', instancer returned nullptr.", translated_data.c_str());
 			return false;
 		}
 
 		// Assign the element its text value.
-		ElementText* text_element = rmlui_dynamic_cast< ElementText* >(element_ptr.get());
+		ElementText* text_element = rmlui_dynamic_cast< ElementText* >(element.get());
 		if (!text_element)
 		{
-			Log::Message(Log::LT_ERROR, "Failed to instance text element '%s'. Found type '%s', was expecting a derivative of ElementText.", translated_data.c_str(), rmlui_type_name(*element_ptr));
+			Log::Message(Log::LT_ERROR, "Failed to instance text element '%s'. Found type '%s', was expecting a derivative of ElementText.", translated_data.c_str(), rmlui_type_name(*element));
 			return false;
 		}
 
-		// Add to active node.
-		Element* element = parent->AppendChild(std::move(element_ptr));
+		text_element->SetText(translated_data);
 
-		// See if this text element uses data bindings.
-		bool data_view_added = false;
-		if (DataModel* data_model = element->data_model)
-		{
-			const size_t i_brackets = translated_data.find("{{", 0);
-			if (i_brackets != String::npos)
-			{
-				auto view = std::make_unique<DataViewText>(*data_model, text_element, translated_data, i_brackets);
-				if (*view)
-				{
-					data_model->AddView(std::move(view));
-					data_view_added = true;
-				}
-				else
-				{
-					Log::Message(Log::LT_WARNING, "Could not add data binding view to element '%s'.", parent->GetAddress().c_str());
-				}
-			}
-		}
-
-		if(!data_view_added)
-			text_element->SetText(translated_data);
+		// Add to active node.
+		parent->AppendChild(std::move(element));
 	}
 
 	return true;

+ 1 - 0
Source/Core/XMLNodeHandlerDefault.cpp

@@ -89,6 +89,7 @@ bool XMLNodeHandlerDefault::ElementData(XMLParser* parser, const String& data)
 	
 	RMLUI_ASSERT(parent);
 
+	// TODO: Move somewhere else. Perhaps spawn a sub-element, or set an attribute with the rml contents.
 	if (auto data_model = parent->GetDataModel())
 	{
 		if(auto attribute = parent->GetAttribute("data-for"))