瀏覽代碼

Transitions on class change.

Michael 7 年之前
父節點
當前提交
35381790cc

+ 1 - 0
Include/Rocket/Core/ElementUtilities.h

@@ -39,6 +39,7 @@ namespace Core {
 class Context;
 class FontFaceHandle;
 class RenderInterface;
+class ViewState;
 
 /**
 	Utility functions for dealing with elements.

+ 3 - 0
Include/Rocket/Core/Property.h

@@ -105,6 +105,9 @@ public:
 		return value.Get<T>();
 	}
 
+	bool operator==(const Property& other) const { return unit == other.unit && value == other.value; }
+	bool operator!=(const Property& other) const { return !(*this == other); }
+
 	Variant value;
 	Unit unit;
 	int specificity;

+ 2 - 0
Include/Rocket/Core/Reference.h

@@ -83,6 +83,8 @@ public:
 	ReferenceType* operator->() throw()
 		{ return object; }
 
+	bool operator==(const ThisType& other) const { return object == other.object; }
+	bool operator!=(const ThisType& other) const { return object != other.object; }
 private:
 	mutable ReferenceType *object;
 };

+ 0 - 1
Include/Rocket/Core/Transform.h

@@ -34,7 +34,6 @@
 namespace Rocket {
 namespace Core {
 
-class ViewState;
 namespace Transforms { struct Primitive; }
 class Property;
 

+ 6 - 0
Include/Rocket/Core/Transition.h

@@ -53,6 +53,12 @@ struct TransitionList {
 };
 
 
+inline bool operator==(const Transition& a, const Transition& b) { return a.name == b.name && a.tween == b.tween && a.duration == b.duration && a.delay == b.delay && a.reverse_adjustment_factor == b.reverse_adjustment_factor; }
+inline bool operator!=(const Transition& a, const Transition& b) { return !(a == b); }
+inline bool operator==(const TransitionList& a, const TransitionList& b) { return a.none == b.none && a.all == b.all && a.transitions == b.transitions; }
+inline bool operator!=(const TransitionList& a, const TransitionList& b) { return !(a == b); }
+
+
 }
 }
 

+ 2 - 0
Include/Rocket/Core/Tween.h

@@ -128,6 +128,8 @@ public:
 		return 1.f - cos(t*Math::ROCKET_PI*0.5f);
 	}
 
+	bool operator==(const Tween& other) const { return type_in == other.type_in && type_out == other.type_out && callback == other.callback; }
+	bool operator!=(const Tween& other) const { return !(*this == other); }
 
 private:
 

+ 3 - 0
Include/Rocket/Core/Variant.h

@@ -113,6 +113,9 @@ public:
 	/// @param[in] copy Variant to share data.
 	Variant& operator=(const Variant& copy);
 
+	bool operator==(const Variant& other) const;
+	bool operator!=(const Variant& other) const { return !(*this == other); }
+
 private:
 
 	/// Sets a byte value on this variant.

+ 4 - 2
Samples/basic/animation/data/animation.rml

@@ -97,10 +97,12 @@
 			transform: scale(1.5);
 		} 
 		#transition_class {
-			transition: margin-left 1s cubic-in-out;
+			margin-left: 50px;
+			transition: all 0.5s cubic-out;
 		}
-		#transition_class.blue {
+		#transition_class.move_me {
 			margin-left: -50px;
+			font-size: 22px;
 		}
 	</style>
 </head>

+ 1 - 2
Samples/basic/animation/src/main.cpp

@@ -248,8 +248,7 @@ public:
 			auto el = event.GetTargetElement();
 			if (el->GetId() == "transition_class")
 			{
-				// TODO: Doesn't seem to properly animate
-				el->SetClass("blue", !el->IsClassSet("blue"));
+				el->SetClass("move_me", !el->IsClassSet("move_me"));
 			}
 		}
 	}

+ 5 - 0
Source/Core/Element.cpp

@@ -991,6 +991,11 @@ bool Element::StartTransition(const Transition & transition, const Property& sta
 
 	bool result = animation->AddKey(duration, target_value, *this, transition.tween);
 
+	if (result)
+	{
+		SetProperty(transition.name, start_value);
+	}
+
 	return result;
 }
 

+ 2 - 2
Source/Core/ElementDefinition.h

@@ -47,9 +47,9 @@ class FontEffect;
 // one. This makes it much more straight-forward to query at run-time.
 typedef std::pair< StringList, Property > PseudoClassProperty;
 typedef std::vector< PseudoClassProperty > PseudoClassPropertyList;
-typedef std::map< String, PseudoClassPropertyList > PseudoClassPropertyDictionary;
+typedef std::unordered_map< String, PseudoClassPropertyList > PseudoClassPropertyDictionary;
 
-typedef std::map< String, Decorator* > DecoratorMap;
+typedef std::unordered_map< String, Decorator* > DecoratorMap;
 typedef std::map< StringList, DecoratorMap > PseudoClassDecoratorMap;
 
 /**

+ 47 - 133
Source/Core/ElementStyle.cpp

@@ -93,7 +93,7 @@ const ElementDefinition* ElementStyle::GetDefinition()
 
 
 // Returns one of this element's properties.
-static const Property* GetLocalProperty(const String& name, PropertyDictionary* local_properties, ElementDefinition* definition, const PseudoClassList& pseudo_classes)
+const Property* ElementStyle::GetLocalProperty(const String& name, PropertyDictionary* local_properties, ElementDefinition* definition, const PseudoClassList& pseudo_classes)
 {
 	// Check for overriding local properties.
 	if (local_properties != NULL)
@@ -111,7 +111,7 @@ static const Property* GetLocalProperty(const String& name, PropertyDictionary*
 }
 
 // Returns one of this element's properties.
-static const Property* GetProperty(const String& name, Element* element, PropertyDictionary* local_properties, ElementDefinition* definition, const PseudoClassList& pseudo_classes)
+const Property* ElementStyle::GetProperty(const String& name, Element* element, PropertyDictionary* local_properties, ElementDefinition* definition, const PseudoClassList& pseudo_classes)
 {
 	if (prop_counter.find(name) == prop_counter.end())
 		prop_counter[name] = 0;
@@ -144,56 +144,53 @@ static const Property* GetProperty(const String& name, Element* element, Propert
 	return property->GetDefaultValue();
 }
 
-// For the properties which are defined in a transition on the given element, apply transition to all properties changed due to a class change.
-// Call this before new_definition has been assigned to the element and its style.
+// Apply transition to relevant properties if a transition is defined on element.
 // Properties that are part of a transition are removed from the properties list.
-static void TransitionUpdatedClass(ElementStyle* style, Element* element, PropertyNameList& properties, ElementDefinition* old_definition, ElementDefinition* new_definition, PropertyDictionary* local_properties, const PseudoClassList& pseudo_classes)
+void ElementStyle::TransitionPropertyChanges(Element* element, PropertyNameList& properties, PropertyDictionary* local_properties, ElementDefinition* old_definition, ElementDefinition* new_definition,
+	const PseudoClassList& pseudo_classes_before, const PseudoClassList& pseudo_classes_after)
 {
-	if (!element || !old_definition || !new_definition || properties.empty())
+	ROCKET_ASSERT(element);
+	if (!old_definition || !new_definition || properties.empty())
 		return;
 
-	if (const Property* transition_property = GetLocalProperty(TRANSITION, local_properties, new_definition, pseudo_classes))
+	if (const Property* transition_property = GetLocalProperty(TRANSITION, local_properties, new_definition, pseudo_classes_after))
 	{
 		auto transition_list = transition_property->Get<TransitionList>();
 
-		if (!transition_list.none && new_definition)
+		if (!transition_list.none)
 		{
-			for (auto it = properties.begin(); it != properties.end();)
-			{
-				auto& property = *it;
-				Transition* transition = nullptr;
+			auto add_transition = [&](const Transition& transition) {
 				bool transition_added = false;
+				const Property* start_value = GetProperty(transition.name, element, local_properties, old_definition, pseudo_classes_before);
+				const Property* target_value = GetProperty(transition.name, element, nullptr, new_definition, pseudo_classes_after);
+				if (start_value && target_value && (*start_value != *target_value))
+					transition_added = element->StartTransition(transition, *start_value, *target_value);
+				return transition_added;
+			};
+
 
-				if (transition_list.all)
+			if (transition_list.all)
+			{
+				Transition transition = transition_list.transitions[0];
+				for (auto it = properties.begin(); it != properties.end(); )
 				{
-					transition = &transition_list.transitions[0];
+					transition.name = *it;
+					if (add_transition(transition))
+						it = properties.erase(it);
+					else
+						++it;
 				}
-				else
+			}
+			else
+			{
+				for (auto& transition : transition_list.transitions)
 				{
-					// TODO: Swap the loops because a lookup in properties is constant time
-					for (auto& transition_candidate : transition_list.transitions) {
-						if (transition_candidate.name == property) {
-							transition = &transition_candidate;
-							break;
-						}
+					if (auto it = properties.find(transition.name); it != properties.end())
+					{
+						if (add_transition(transition))
+							properties.erase(it);
 					}
 				}
-
-				if (transition)
-				{
-					const Property* start_value = GetProperty(property, element, local_properties, old_definition, pseudo_classes);
-					const Property* target_value = GetProperty(property, element, local_properties, new_definition, pseudo_classes);
-
-					// TODO: See if the start and target_value are equal, if so, skip.
-
-					if (start_value && target_value)
-						transition_added = element->StartTransition(*transition, *start_value, *target_value);
-				}
-
-				if (transition_added)
-					it = properties.erase(it);
-				else
-					++it;
 			}
 		}
 	}
@@ -224,7 +221,7 @@ void ElementStyle::UpdateDefinition()
 			if (new_definition != NULL)
 				new_definition->GetDefinedProperties(properties, pseudo_classes);
 
-			TransitionUpdatedClass(this, element, properties, definition, new_definition, local_properties, pseudo_classes);
+			TransitionPropertyChanges(element, properties, local_properties, definition, new_definition, pseudo_classes, pseudo_classes);
 
 			if (definition != NULL)
 				definition->RemoveReference();
@@ -265,65 +262,17 @@ void ElementStyle::SetPseudoClass(const String& pseudo_class, bool activate)
 	else
 		pseudo_classes.erase(pseudo_class);
 
-
-	// Apply transition if defined on this element
-	// Note: We had to set the pseudo classes first so that any transition being overriden in the pseudo class is captured
-	if (const Property* property = GetLocalProperty(TRANSITION))
-	{
-		ROCKET_ASSERT(property->unit == Property::TRANSITION);
-		auto transition_list = property->Get<TransitionList>();
-		const ElementDefinition* definition = element->GetDefinition();
-
-		if (!transition_list.none && definition != NULL)
-		{
-			PropertyNameList properties;
-			definition->GetDefinedProperties(properties, pseudo_classes, pseudo_class);
-
-			for (auto& property : properties)
-			{
-				Transition* transition = nullptr;
-				if (transition_list.all)
-				{
-					transition = &transition_list.transitions[0];
-				}
-				else
-				{
-					for (auto& transition_candidate : transition_list.transitions) {
-						if (transition_candidate.name == property) {
-							transition = &transition_candidate;
-							break;
-						}
-					}
-				}
-
-				if (transition)
-				{
-					// Get start value from local properties first, else, we fetch the property given the previous pseudo classes
-					const Property* start_value = nullptr;
-					if (local_properties)
-						start_value = local_properties->GetProperty(property);
-					if(!start_value) 
-						start_value = definition->GetProperty(property, pseudo_classes_before);
-
-					auto target_value = definition->GetProperty(property, pseudo_classes);
-
-					if (start_value && target_value)
-						element->StartTransition(*transition, *start_value, *target_value);
-				}
-			}
-		}
-	}
-
-
 	if (pseudo_classes.size() != num_pseudo_classes)
 	{
 		element->GetElementDecoration()->DirtyDecorators();
 
-		const ElementDefinition* definition = element->GetDefinition();
 		if (definition != NULL)
 		{
 			PropertyNameList properties;
 			definition->GetDefinedProperties(properties, pseudo_classes, pseudo_class);
+
+			TransitionPropertyChanges(element, properties, local_properties, definition, definition, pseudo_classes_before, pseudo_classes);
+
 			DirtyProperties(properties);
 
 			switch (definition->GetPseudoClassVolatility(pseudo_class))
@@ -434,6 +383,7 @@ bool ElementStyle::SetProperty(const String& name, const Property& property)
 	if (new_property.definition == NULL)
 		return false;
 
+	sizeof(ElementDefinition);
 	if (local_properties == NULL)
 		local_properties = new PropertyDictionary();
 
@@ -461,53 +411,13 @@ void ElementStyle::RemoveProperty(const String& name)
 // Returns one of this element's properties.
 const Property* ElementStyle::GetProperty(const String& name)
 {
-	if (prop_counter.find(name) == prop_counter.end())
-		prop_counter[name] = 0;
-	prop_counter[name] = prop_counter[name] + 1;
-
-	const Property* local_property = GetLocalProperty(name);
-	if (local_property != NULL)
-		return local_property;
-
-	// Fetch the property specification.
-	const PropertyDefinition* property = StyleSheetSpecification::GetProperty(name);
-	if (property == NULL)
-		return NULL;
-
-	// If we can inherit this property, return our parent's property.
-	if (property->IsInherited())
-	{
-		Element* parent = element->GetParentNode();
-		while (parent != NULL)
-		{
-			const Property* parent_property = parent->style->GetLocalProperty(name);
-			if (parent_property)
-				return parent_property;
-			
-			parent = parent->GetParentNode();
-		}
-	}
-
-	// No property available! Return the default value.
-	return property->GetDefaultValue();
+	return GetProperty(name, element, local_properties, definition, pseudo_classes);
 }
 
 // Returns one of this element's properties.
 const Property* ElementStyle::GetLocalProperty(const String& name)
 {
-	// Check for overriding local properties.
-	if (local_properties != NULL)
-	{
-		const Property* property = local_properties->GetProperty(name);
-		if (property != NULL)
-			return property;
-	}
-
-	// Check for a property defined in an RCSS rule.
-	if (definition != NULL)
-		return definition->GetProperty(name, pseudo_classes);
-
-	return NULL;
+	return GetLocalProperty(name, local_properties, definition, pseudo_classes);
 }
 
 float ElementStyle::ResolveLength(const Property * property)
@@ -579,7 +489,7 @@ float ElementStyle::ResolveAngle(const Property * property)
 
 float ElementStyle::ResolveNumericProperty(const String& property_name, const Property * property)
 {
-	if (property->unit & Property::LENGTH)
+	if ((property->unit & Property::LENGTH) && !(property->unit == Property::EM && property_name == FONT_SIZE))
 	{
 		return ResolveLength(property);
 	}
@@ -595,7 +505,8 @@ float ElementStyle::ResolveNumericProperty(const String& property_name, const Pr
 
 float ElementStyle::ResolveNumericProperty(const Property * property, RelativeTarget relative_target)
 {
-	if (property->unit & Property::LENGTH)
+	// There is an exception on font-size properties, as 'em' units here refer to parent font size instead
+	if ((property->unit & Property::LENGTH) && !(property->unit == Property::EM && relative_target == RelativeTarget::ParentFontSize))
 	{
 		return ResolveLength(property);
 	}
@@ -630,10 +541,13 @@ float ElementStyle::ResolveNumericProperty(const Property * property, RelativeTa
 
 	switch (property->unit)
 	{
+	case Property::EM:
 	case Property::NUMBER:
 		scale_value = property->value.Get< float >();
+		break;
 	case Property::PERCENT:
 		scale_value = property->value.Get< float >() * 0.01f;
+		break;
 	}
 
 	return base_value * scale_value;

+ 5 - 0
Source/Core/ElementStyle.h

@@ -222,6 +222,11 @@ private:
 	// Sets a list of our potentially inherited properties as dirtied by an ancestor.
 	void DirtyInheritedProperties(const PropertyNameList& properties);
 
+	static const Property* GetLocalProperty(const String & name, PropertyDictionary * local_properties, ElementDefinition * definition, const PseudoClassList & pseudo_classes);
+	static const Property* GetProperty(const String & name, Element * element, PropertyDictionary * local_properties, ElementDefinition * definition, const PseudoClassList & pseudo_classes);
+	static void TransitionPropertyChanges(Element * element, PropertyNameList & properties, PropertyDictionary * local_properties, ElementDefinition * old_definition, ElementDefinition * new_definition,
+		const PseudoClassList & pseudo_classes_before, const PseudoClassList & pseudo_classes_after);
+
 	// Element these properties belong to
 	Element* element;
 

+ 3 - 2
Source/Core/PropertyParserTransition.cpp

@@ -144,7 +144,7 @@ bool PropertyParserTransition::ParseValue(Property & property, const String & va
 					if (transition_list.transitions.size() > 0) // The all keyword can not be part of multiple definitions
 						return false;
 					transition_list.all = true;
-					transition.name = "all";
+					target_property_names.push_back("all");
 				}
 				else if (it->second.type == TransitionSpec::TWEEN)
 				{
@@ -212,7 +212,8 @@ bool PropertyParserTransition::ParseValue(Property & property, const String & va
 		}
 
 		// Validate the parsed transition
-		if (target_property_names.empty() || transition.duration <= 0.0f || transition.reverse_adjustment_factor < 0.0f || transition.reverse_adjustment_factor > 1.0f)
+		if (target_property_names.empty() || transition.duration <= 0.0f || transition.reverse_adjustment_factor < 0.0f || transition.reverse_adjustment_factor > 1.0f
+			|| (transition_list.all && target_property_names.size() != 1))
 		{
 			return false;
 		}

+ 47 - 0
Source/Core/Variant.cpp

@@ -246,5 +246,52 @@ Variant& Variant::operator=(const Variant& copy)
 	return *this;
 }
 
+#define DEFAULT_VARIANT_COMPARE(TYPE) static_cast<TYPE>(*(TYPE*)data) == static_cast<TYPE>(*(TYPE*)other.data)
+
+bool Variant::operator==(const Variant & other) const
+{
+	if (type != other.type)
+		return false;
+
+	switch (type)
+	{
+	case BYTE:
+		return DEFAULT_VARIANT_COMPARE(byte);
+	case CHAR:
+		return DEFAULT_VARIANT_COMPARE(char);
+	case FLOAT:
+		return DEFAULT_VARIANT_COMPARE(float);
+	case INT:
+		return DEFAULT_VARIANT_COMPARE(int);
+	case STRING:
+		return DEFAULT_VARIANT_COMPARE(String);
+	case WORD:
+		return DEFAULT_VARIANT_COMPARE(word);
+	case VECTOR2:
+		return DEFAULT_VARIANT_COMPARE(Vector2f);
+	case VECTOR3:
+		return DEFAULT_VARIANT_COMPARE(Vector3f);
+	case VECTOR4:
+		return DEFAULT_VARIANT_COMPARE(Vector4f);
+	case TRANSFORMREF:
+		return DEFAULT_VARIANT_COMPARE(TransformRef);
+	case TRANSITIONLIST:
+		return DEFAULT_VARIANT_COMPARE(TransitionList);
+	case COLOURF:
+		return DEFAULT_VARIANT_COMPARE(Colourf);
+	case COLOURB:
+		return DEFAULT_VARIANT_COMPARE(Colourb);
+	case SCRIPTINTERFACE:
+		return DEFAULT_VARIANT_COMPARE(ScriptInterface*);
+	case VOIDPTR:
+		return DEFAULT_VARIANT_COMPARE(void*);
+	case NONE:
+		return true;
+		break;
+	}
+	ROCKET_ERRORMSG("Variant comparison not implemented for this type.");
+	return false;
+}
+
 }
 }