Jelajahi Sumber

RCSS transition beta. Only activated on pseudo-classes for now.

Michael 7 tahun lalu
induk
melakukan
a9388f5229

+ 3 - 0
Build/cmake/FileList.cmake

@@ -65,6 +65,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserNumber.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserString.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransform.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransition.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyShorthandDefinition.h
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamFile.h
     ${PROJECT_SOURCE_DIR}/Source/Core/StringCache.h
@@ -181,6 +182,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/TypeConverter.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/TypeConverter.inl
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Types.h
+    ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Transition.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Tween.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/URL.h
     ${PROJECT_SOURCE_DIR}/Include/Rocket/Core/Variant.h
@@ -294,6 +296,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserNumber.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserString.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransform.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransition.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertySpecification.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ReferenceCountable.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/RenderInterface.cpp

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

@@ -72,6 +72,7 @@
 #include "SystemInterface.h"
 #include "Texture.h"
 #include "Types.h"
+#include "Transition.h"
 #include "Tween.h"
 #include "Vertex.h"
 #include "XMLNodeHandler.h"

+ 1 - 1
Include/Rocket/Core/Element.h

@@ -296,7 +296,7 @@ public:
 	/// If an animation of the same property name exists, the target value and duration will be added as a new animation key, 
 	/// adding to its total duration. Then, num_iterations, alternate_direction and delay will be ignored.
 	/// @return True if a new animation or key was added.
-	bool Animate(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{}, int num_iterations = 1, bool alternate_direction = true, float delay = 0.0f);
+	bool Animate(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{}, int num_iterations = 1, bool alternate_direction = true, float delay = 0.0f, bool replace = false);
 	
 	/// Iterates over the properties defined on this element.
 	/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.

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

@@ -75,6 +75,7 @@ public:
 		PPI_UNIT = INCH | CM | MM | PT | PC,
 
 		TRANSFORM = 1 << 17,			// transform; fetch as < TransformRef >
+		TRANSITION = 1 << 18,           // transition; fetch as < TransitionList >
 
 		LENGTH = PX | DP | PPI_UNIT | EM | REM,
 		LENGTH_PERCENT = LENGTH | PERCENT,

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

@@ -0,0 +1,58 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2018 Michael Ragazzon
+ *
+ * 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.
+ *
+ */
+
+
+
+#ifndef ROCKETCORETRANSITION_H
+#define ROCKETCORETRANSITION_H
+
+
+#include "String.h"
+#include "Tween.h"
+
+namespace Rocket {
+namespace Core {
+
+
+struct Transition {
+	String name;
+	Tween tween;
+	float duration = 0.0f;
+	float delay = 0.0f;
+};
+
+struct TransitionList {
+	bool none = true;
+	bool all = false;
+	std::vector<Transition> transitions;
+};
+
+
+}
+}
+
+#endif

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

@@ -29,6 +29,7 @@
 #define ROCKETCORETYPECONVERTER_H
 
 #include "Types.h"
+#include "Transition.h"
 #include "Log.h"
 #include "Stream.h"
 #include "StringUtilities.h"

+ 1 - 0
Include/Rocket/Core/TypeConverter.inl

@@ -112,6 +112,7 @@ PASS_THROUGH(Colourf);
 PASS_THROUGH(Colourb);
 PASS_THROUGH(String);
 PASS_THROUGH(TransformRef);
+PASS_THROUGH(TransitionList);
 
 // Pointer types need to be typedef'd
 class ScriptInterface;

+ 4 - 0
Include/Rocket/Core/Types.h

@@ -111,6 +111,10 @@ typedef std::vector< ElementAnimation > ElementAnimationList;
 
 // Reference types
 typedef SharedReference< Transform > TransformRef;
+
+struct Transition;
+struct TransitionList;
+
 }
 }
 

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

@@ -76,6 +76,7 @@ public:
 		COLOURB = 'h',
 		SCRIPTINTERFACE = 'p',
 		TRANSFORMREF = 't',
+		TRANSITIONLIST = 'T',
 		VOIDPTR = '*',			
 	};
 
@@ -150,6 +151,9 @@ private:
 	/// Sets a TransformRef value on this variant.
 	/// @param[in] value New value to set.
 	void Set(const TransformRef& value);
+	/// Sets a TransitionList value on this variant.
+	/// @param[in] value New value to set.
+	void Set(const TransitionList& value);
 	/// Sets a Colourf value on this variant.
 	/// @param[in] value New value to set.
 	void Set(const Colourf& value);

+ 4 - 0
Include/Rocket/Core/Variant.inl

@@ -92,6 +92,10 @@ bool Variant::GetInto(T& value) const
 			return TypeConverter< TransformRef, T >::Convert(*(TransformRef*)data, value);
 		break;
 
+		case TRANSITIONLIST:
+			return TypeConverter< TransitionList, T >::Convert(*(TransitionList*)data, value);
+		break;
+
 		case COLOURF:
 			return TypeConverter< Colourf, T >::Convert(*(Colourf*)data, value);
 		break;

+ 9 - 1
Samples/basic/animation/data/animation.rml

@@ -85,6 +85,14 @@
 		#text_align {
 			text-align: left;
 		}
+		#transition_test {
+			transition: padding-left 1.6s elastic-out, background-color 1.2s quadratic-out, transform 1.3s exponential-out;
+			transform: scale(1.0);
+		}
+		#transition_test:hover {
+			padding-left: 60px;
+			transform: scale(1.5);
+		} 
 	</style>
 </head>
 
@@ -109,7 +117,7 @@
 	<div style="font-size: 1.5em; text-align: left;">Mixed units tests</div>
 	<div class="container"><div class="plain" id="abs_rel">Pixel vs percentage.</div></div>
 	<div class="container"><div class="plain" id="abs_rel_transform">Pixel vs percentage transform.</div></div>
-	<div class="container"><div class="plain" id="text_align">Text align</div></div>
+	<div class="container"><div class="plain" id="transition_test">Transition test (hover)</div></div>
 </div>
 </body>
 </rml>

+ 6 - 1
Source/Core/Element.cpp

@@ -911,7 +911,7 @@ const Vector2f Element::Project(const Vector2f& point) noexcept
 	}
 }
 
-bool Element::Animate(const String & property_name, const Property & target_value, float duration, Tween tween, int num_iterations, bool alternate_direction, float delay)
+bool Element::Animate(const String & property_name, const Property & target_value, float duration, Tween tween, int num_iterations, bool alternate_direction, float delay, bool replace)
 {
 	if (delay < 0.0f)
 		return false;
@@ -941,6 +941,11 @@ bool Element::Animate(const String & property_name, const Property & target_valu
 		);
 		animation = &animations.back();
 	}
+	else if (replace)
+	{
+		animation->ReverseAnimation();
+		animation = nullptr;
+	}
 	else
 	{
 		target_time += animation->GetDuration();

+ 5 - 3
Source/Core/ElementAnimation.cpp

@@ -361,7 +361,7 @@ static bool PrepareTransforms(std::vector<AnimationKey>& keys, Element& element,
 ElementAnimation::ElementAnimation(const String& property_name, const Property& current_value, float start_world_time, float duration, int num_iterations, bool alternate_direction)
 	: property_name(property_name), duration(duration), num_iterations(num_iterations), alternate_direction(alternate_direction),
 	keys({ AnimationKey{0.0f, current_value, Tween{}} }),
-	last_update_world_time(start_world_time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false)
+	last_update_world_time(start_world_time), time_since_iteration_start(0.0f), current_iteration(0), reverse_direction(false), animation_complete(false), reverse_animation(false)
 {
 	ROCKET_ASSERT(current_value.definition);
 }
@@ -421,9 +421,9 @@ Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& eleme
 	if (time_since_iteration_start >= duration)
 	{
 		// Next iteration
-		current_iteration += 1;
+		current_iteration += (reverse_animation ? -1 : 1);
 
-		if (current_iteration < num_iterations || num_iterations == -1)
+		if (num_iterations == -1 || (current_iteration >= 0 && current_iteration < num_iterations) )
 		{
 			time_since_iteration_start -= duration;
 
@@ -441,6 +441,8 @@ Property ElementAnimation::UpdateAndGetProperty(float world_time, Element& eleme
 
 	if (reverse_direction)
 		t = duration - t;
+	if (reverse_animation)
+		t = duration - t;
 
 	int key0 = -1;
 	int key1 = -1;

+ 2 - 0
Source/Core/ElementAnimation.h

@@ -60,6 +60,7 @@ private:
 	bool reverse_direction;
 
 	bool animation_complete;
+	bool reverse_animation;
 
 public:
 
@@ -73,6 +74,7 @@ public:
 	float GetDuration() const { return duration; }
 	void SetDuration(float duration) { this->duration = duration; }
 	bool IsComplete() const { return animation_complete; }
+	void ReverseAnimation() { reverse_animation = !reverse_animation; }
 };
 
 

+ 54 - 10
Source/Core/ElementStyle.cpp

@@ -140,15 +140,62 @@ void ElementStyle::UpdateDefinition()
 	}
 }
 
+
+
 // Sets or removes a pseudo-class on the element.
 void ElementStyle::SetPseudoClass(const String& pseudo_class, bool activate)
 {
 	size_t num_pseudo_classes = pseudo_classes.size();
 
+	auto pseudo_classes_after = pseudo_classes;
+
 	if (activate)
-		pseudo_classes.insert(pseudo_class);
+		pseudo_classes_after.insert(pseudo_class);
 	else
-		pseudo_classes.erase(pseudo_class);
+		pseudo_classes_after.erase(pseudo_class);
+
+
+	// Apply transition if set
+	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_after, 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)
+				{
+					auto target_property = definition->GetProperty(property, pseudo_classes_after);
+					if (target_property)
+						element->Animate(property, *target_property, transition->duration, transition->tween, 1, false, transition->delay, true);
+				}
+			}
+		}
+	}
+
+	pseudo_classes.swap(pseudo_classes_after);
+
 
 	if (pseudo_classes.size() != num_pseudo_classes)
 	{
@@ -775,15 +822,12 @@ void ElementStyle::DirtyProperties(const PropertyNameList& properties, bool clea
 		StyleSheetSpecification::GetRegisteredProperties() == properties ||
 		StyleSheetSpecification::GetRegisteredInheritedProperties() == properties;
 
+
 	if (all_inherited_dirty)
 	{
 		const PropertyNameList &all_inherited_properties = StyleSheetSpecification::GetRegisteredInheritedProperties();
 		for (int i = 0; i < element->GetNumChildren(true); i++)
 			element->GetChild(i)->GetStyle()->DirtyInheritedProperties(all_inherited_properties);
-
-		// Clear all cached properties.
-		cache->Clear();
-		cache->ClearInherited();
 	}
 	else
 	{
@@ -804,12 +848,12 @@ void ElementStyle::DirtyProperties(const PropertyNameList& properties, bool clea
 			for (int i = 0; i < element->GetNumChildren(true); i++)
 				element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
 		}
-
-		// Clear cached properties.
-		cache->Clear();
-		cache->ClearInherited();
 	}
 
+	// Clear all cached properties.
+	cache->Clear();
+	cache->ClearInherited();
+
 	// clear the list of EM-properties, we will refill it in DirtyEmProperties
 	if (clear_em_properties && em_properties != NULL)
 	{

+ 1 - 0
Source/Core/PropertyParserTransform.cpp

@@ -27,6 +27,7 @@
 
 #include "precompiled.h"
 #include "PropertyParserTransform.h"
+#include "../../Include/Rocket/Core/TransformPrimitive.h"
 
 namespace Rocket {
 namespace Core {

+ 3 - 2
Source/Core/PropertyParserTransform.h

@@ -29,18 +29,19 @@
 #define ROCKETCOREPROPERTYPARSERTRANSFORM_H
 
 #include "../../Include/Rocket/Core/PropertyParser.h"
-#include "../../Include/Rocket/Core/TransformPrimitive.h"
 #include "PropertyParserNumber.h"
 
 namespace Rocket {
 namespace Core {
 
+namespace Transforms { struct NumericValue; }
+
+
 /**
 	A property parser that parses a RCSS transform property specification.
 
 	@author Markus Schöngart
  */
-
 class PropertyParserTransform : public PropertyParser
 {
 public:

+ 196 - 0
Source/Core/PropertyParserTransition.cpp

@@ -0,0 +1,196 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2018 Michael Ragazzon
+ *
+ * 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 "precompiled.h"
+#include "PropertyParserTransition.h"
+#include "../../Include/Rocket/Core/StringUtilities.h"
+
+
+namespace Rocket {
+namespace Core {
+
+
+struct TransitionSpec {
+	enum Type { KEYWORD_NONE, KEYWORD_ALL, TWEEN } type;
+	Tween tween;
+	TransitionSpec(Tween tween) : type(TWEEN), tween(tween) {}
+	TransitionSpec(Type type) : type(type) {}
+};
+
+
+static const std::unordered_map<String, TransitionSpec> transition_spec = {
+		{"none", {TransitionSpec::KEYWORD_NONE} },
+		{"all", {TransitionSpec::KEYWORD_ALL}},
+
+		{"back-in", {Tween{Tween::Back, Tween::In}}},
+		{"back-out", {Tween{Tween::Back, Tween::Out}}},
+		{"back-in-out", {Tween{Tween::Back, Tween::InOut}}},
+
+		{"bounce-in", {Tween{Tween::Bounce, Tween::In}}},
+		{"bounce-out", {Tween{Tween::Bounce, Tween::Out}}},
+		{"bounce-in-out", {Tween{Tween::Bounce, Tween::InOut}}},
+
+		{"circular-in", {Tween{Tween::Circular, Tween::In}}},
+		{"circular-out", {Tween{Tween::Circular, Tween::Out}}},
+		{"circular-in-out", {Tween{Tween::Circular, Tween::InOut}}},
+
+		{"cubic-in", {Tween{Tween::Cubic, Tween::In}}},
+		{"cubic-out", {Tween{Tween::Cubic, Tween::Out}}},
+		{"cubic-in-out", {Tween{Tween::Cubic, Tween::InOut}}},
+
+		{"elastic-in", {Tween{Tween::Elastic, Tween::In}}},
+		{"elastic-out", {Tween{Tween::Elastic, Tween::Out}}},
+		{"elastic-in-out", {Tween{Tween::Elastic, Tween::InOut}}},
+
+		{"exponential-in", {Tween{Tween::Exponential, Tween::In}}},
+		{"exponential-out", {Tween{Tween::Exponential, Tween::Out}}},
+		{"exponential-in-out", {Tween{Tween::Exponential, Tween::InOut}}},
+
+		{"linear-in", {Tween{Tween::Linear, Tween::In}}},
+		{"linear-out", {Tween{Tween::Linear, Tween::Out}}},
+		{"linear-in-out", {Tween{Tween::Linear, Tween::InOut}}},
+
+		{"quadratic-in", {Tween{Tween::Quadratic, Tween::In}}},
+		{"quadratic-out", {Tween{Tween::Quadratic, Tween::Out}}},
+		{"quadratic-in-out", {Tween{Tween::Quadratic, Tween::InOut}}},
+
+		{"quartic-in", {Tween{Tween::Quartic, Tween::In}}},
+		{"quartic-out", {Tween{Tween::Quartic, Tween::Out}}},
+		{"quartic-in-out", {Tween{Tween::Quartic, Tween::InOut}}},
+
+		{"quintic-in", {Tween{Tween::Quintic, Tween::In}}},
+		{"quintic-out", {Tween{Tween::Quintic, Tween::Out}}},
+		{"quintic-in-out", {Tween{Tween::Quintic, Tween::InOut}}},
+
+		{"sine-in", {Tween{Tween::Sine, Tween::In}}},
+		{"sine-out", {Tween{Tween::Sine, Tween::Out}}},
+		{"sine-in-out", {Tween{Tween::Sine, Tween::InOut}}},
+};
+
+
+
+
+PropertyParserTransition::PropertyParserTransition()
+{
+}
+
+
+bool PropertyParserTransition::ParseValue(Property & property, const String & value, const ParameterMap & parameters) const
+{
+	StringList list_of_properties;
+	{
+		auto lowercase_value = value.ToLower();
+		StringUtilities::ExpandString(list_of_properties, lowercase_value, ',');
+	}
+
+	TransitionList transition_list{ false, false, {} };
+
+	for (const String& single_property : list_of_properties)
+	{
+		Transition transition;
+
+		StringList arguments;
+		StringUtilities::ExpandString(arguments, single_property, ' ');
+
+		bool duration_found = false;
+		bool delay_found = false;
+
+		for (auto& argument : arguments)
+		{
+			if (argument.Empty())
+				continue;
+
+			// See if we have a <keyword> or <tween> specifier as defined in transition_spec
+			if (auto it = transition_spec.find(argument); it != transition_spec.end())
+			{
+				if (it->second.type == TransitionSpec::KEYWORD_NONE)
+				{
+					if (transition_list.transitions.size() > 0) // The none keyword can not be part of multiple definitions
+						return false;
+					property = Property{ TransitionList{true, false, {}}, Property::TRANSITION };
+					return true;
+				}
+				else if (it->second.type == TransitionSpec::KEYWORD_ALL)
+				{
+					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";
+				}
+				else if (it->second.type == TransitionSpec::TWEEN)
+				{
+					transition.tween = it->second.tween;
+				}
+			}
+			else
+			{
+				// Either <duration>, <delay> or a <property name>
+
+				float* time_target = (duration_found ? &transition.delay : &transition.duration);
+
+				if (sscanf(argument.CString(), "%fs", time_target) == 1)
+				{
+					// Duration or delay was assigned
+					if (!duration_found)
+						duration_found = true;
+					else if (!delay_found)
+						delay_found = true;
+					else
+						return false;
+				}
+				else
+				{
+					// Must be a property name (unless its invalid, we don't actually validate it)
+					if (!transition.name.Empty())
+						return false;
+					transition.name = argument;
+				}
+			}
+		}
+
+		// Validate the parsed transition
+		if (transition.name.Length() == 0 || transition.duration <= 0.0f)
+		{
+			return false;
+		}
+
+		transition_list.transitions.push_back(transition);
+	}
+
+	property = Property{ transition_list, Property::TRANSITION };
+
+	return true;
+}
+
+void PropertyParserTransition::Release()
+{
+	delete this;
+}
+
+}
+}

+ 85 - 0
Source/Core/PropertyParserTransition.h

@@ -0,0 +1,85 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2018 Michael Ragazzon
+ *
+ * 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.
+ *
+ */
+
+
+
+#ifndef ROCKETCOREPROPERTYPARSERTRANSITION_H
+#define ROCKETCOREPROPERTYPARSERTRANSITION_H
+
+
+#include "../../Include/Rocket/Core/PropertyParser.h"
+
+namespace Rocket {
+namespace Core {
+
+
+
+/**
+A property parser that parses a RCSS transition property specification.
+*/
+
+class PropertyParserTransition : public PropertyParser
+{
+public:
+	PropertyParserTransition();
+
+	/// Called to parse a RCSS transition declaration.
+	/// @param[out] property The property to set the parsed value on.
+	/// @param[in] value The raw value defined for this property.
+	/// @param[in] parameters The parameters defined for this property.
+	/// @return True if the value was validated successfully, false otherwise.
+	bool ParseValue(Property& property, const String& value, const ParameterMap& parameters) const override;
+
+	// Destroys the parser.
+	void Release() override;
+
+private:
+
+
+	/// Scan a string for a parameterized keyword with a certain number of numeric arguments.
+	/// @param[in] str The string to search for the parameterized keyword
+	/// @param[in] keyword The name of the keyword to search for
+	/// @param[in] parsers The numeric argument parsers
+	/// @param[out] args The numeric arguments encountered
+	/// @param[in] nargs The number of numeric arguments expected
+	/// @returns The number of bytes read, if the function call occurs at the beginning of str, 0 otherwise.
+	//int Scan(const char* str, const char* keyword, const PropertyParser** parsers, Transforms::NumericValue* args, int nargs) const override;
+
+	//PropertyParserNumber number, length, angle;
+};
+
+
+
+
+
+
+}
+}
+
+
+
+#endif

+ 1 - 0
Source/Core/StringCache.cpp

@@ -104,6 +104,7 @@ const String TRANSFORM_ORIGIN = "transform-origin";
 const String TRANSFORM_ORIGIN_X = "transform-origin-x";
 const String TRANSFORM_ORIGIN_Y = "transform-origin-y";
 const String TRANSFORM_ORIGIN_Z = "transform-origin-z";
+const String TRANSITION = "transition";
 const String SCROLL_DEFAULT_STEP_SIZE = "scroll-default-step-size";
 const String OPACITY = "opacity";
 const String POINTER_EVENTS = "pointer-events";

+ 2 - 0
Source/Core/StringCache.h

@@ -109,6 +109,8 @@ extern const String TRANSFORM_ORIGIN_X;
 extern const String TRANSFORM_ORIGIN_Y;
 extern const String TRANSFORM_ORIGIN_Z;
 
+extern const String TRANSITION;
+
 extern const String SCROLL_DEFAULT_STEP_SIZE;
 extern const String OPACITY;
 extern const String POINTER_EVENTS;

+ 3 - 0
Source/Core/StyleSheetSpecification.cpp

@@ -33,6 +33,7 @@
 #include "PropertyParserKeyword.h"
 #include "PropertyParserString.h"
 #include "PropertyParserTransform.h"
+#include "PropertyParserTransition.h"
 
 namespace Rocket {
 namespace Core {
@@ -147,6 +148,7 @@ void StyleSheetSpecification::RegisterDefaultParsers()
 	RegisterParser("angle", new PropertyParserNumber(Property::ANGLE, Property::RAD));
 	RegisterParser("keyword", new PropertyParserKeyword());
 	RegisterParser("string", new PropertyParserString());
+	RegisterParser("transition", new PropertyParserTransition());
 	RegisterParser(COLOR, new PropertyParserColour());
 	RegisterParser(TRANSFORM, new PropertyParserTransform());
 }
@@ -280,6 +282,7 @@ void StyleSheetSpecification::RegisterDefaultProperties()
 	RegisterProperty(TRANSFORM_ORIGIN_Z, "0", false, false).AddParser("length");
 	RegisterShorthand(TRANSFORM_ORIGIN, "transform-origin-x, transform-origin-y, transform-origin-z");
 
+	RegisterProperty(TRANSITION, "none", false, false).AddParser("transition");
 }
 
 }

+ 29 - 2
Source/Core/Variant.cpp

@@ -27,6 +27,7 @@
 
 #include "precompiled.h"
 #include "../../Include/Rocket/Core/Variant.h"
+#include "PropertyParserTransition.h"
 
 namespace Rocket {
 namespace Core {
@@ -37,6 +38,7 @@ Variant::Variant() : type(NONE)
 	ROCKET_STATIC_ASSERT(sizeof(Colourb) <= LOCAL_DATA_SIZE, LOCAL_DATA_TOO_SMALL_FOR_Colourb);
 	ROCKET_STATIC_ASSERT(sizeof(Colourf) <= LOCAL_DATA_SIZE, LOCAL_DATA_TOO_SMALL_FOR_Colourf);
 	ROCKET_STATIC_ASSERT(sizeof(String) <= LOCAL_DATA_SIZE, LOCAL_DATA_TOO_SMALL_FOR_String);
+	ROCKET_STATIC_ASSERT(sizeof(TransitionList) <= LOCAL_DATA_SIZE, LOCAL_DATA_TOO_SMALL_FOR_TRANSITION_LIST);
 }
 
 Variant::Variant( const Variant& copy ) : type(NONE)
@@ -61,7 +63,6 @@ void Variant::Clear()
 			string->~String();
 		}
 		break;
-
 		case TRANSFORMREF:
 		{
 			// Clean up the transform.
@@ -69,7 +70,13 @@ void Variant::Clear()
 			transform->~TransformRef();
 		}
 		break;
-
+		case TRANSITIONLIST:
+		{
+			// Clean up the transition list.
+			TransitionList* transition_list = (TransitionList*)data;
+			transition_list->~TransitionList();
+		}
+		break;
 		default:
 		break;
 	}
@@ -102,6 +109,13 @@ void Variant::Set(const Variant& copy)
 		}
 		break;
 
+		case TRANSITIONLIST:
+		{
+			// Create the transition list
+			Set(*(TransitionList*)copy.data);
+		}
+		break;
+
 		default:
 			Clear();
 			memcpy(data, copy.data, LOCAL_DATA_SIZE);
@@ -195,6 +209,19 @@ void Variant::Set(const TransformRef& value)
 	}
 }
 
+void Variant::Set(const TransitionList& value)
+{
+	if (type == TRANSITIONLIST)
+	{
+		*(TransitionList*)data = value;
+	}
+	else
+	{
+		type = TRANSITIONLIST;
+		new(data) TransitionList(value);
+	}
+}
+
 void Variant::Set(const Colourf& value)
 {
 	type = COLOURF;