Преглед изворни кода

Update demo sample with animations.

Michael Ragazzon пре 6 година
родитељ
комит
bd8b042bee

+ 1 - 1
Include/RmlUi/Core/EventListener.h

@@ -59,7 +59,7 @@ public:
 		RMLUI_UNUSED(element);
 	}
 
-	/// Called when the listener has been detached from a Element
+	/// Called when the listener has been detached from an Element
 	virtual void OnDetach(Element* RMLUI_UNUSED_PARAMETER(element))
 	{
 		RMLUI_UNUSED(element);

+ 1 - 1
Samples/assets/invader.rcss

@@ -21,7 +21,7 @@
 	icon-game:    230px 152px 51px 39px;
 	icon-hiscore: 281px 152px 51px 39px;
 	icon-waves:   332px 152px 51px 39px;
-	icon-flag:    332px 191px 51px 39px;
+	icon-flag:    336px 191px 51px 39px;
 	icon-lives:   383px 152px 51px 39px;
 	icon-score:   434px 152px 51px 39px;
 	

+ 211 - 13
Samples/basic/demo/data/demo.rml

@@ -72,6 +72,10 @@ panel
 	margin-left: auto;
 	margin-right: auto;
 }
+h1
+{
+	margin: 1.4em 0 0.7em;
+}
 .minipanel
 {
 	max-width: 500px;
@@ -81,10 +85,17 @@ p.emojis
 	font-size: 35px;
 	color: #b33;
 }
-
+.clickable
+{ 
+	cursor: pointer;
+}
 button:focus {
 	image-color: #0ff;
 }
+
+
+/***  Decorators  ***/
+
 #decorators > div {
 	margin-bottom: 14px;
 }
@@ -162,6 +173,140 @@ button:focus {
 .transform.fit-scale-none { decorator: image( icon-invader rotate-180 scale-none ); }
 .transform.fit-scale-down { decorator: image( icon-invader rotate-180 scale-down ); }
 
+
+/***  Animations  ***/
+
+#tweening_area
+{
+	position: relative;
+	margin: 30px auto;
+	width: 400px;
+	height: 250px;
+	border: 1px #777;
+	background-color: #ccc2;
+	cursor: cross;
+}
+#tweening_area img
+{
+	position: absolute;
+	cursor: pointer;
+	transform: translate(190px, 100px);
+}
+
+#transition
+{
+	margin: 20px auto 30px;
+	width: 400px;
+	height: 250px;
+	border: 1px #777;
+	background-color: #ccc2;
+	position: relative;
+	overflow: hidden;
+}
+#transition > img
+{
+	position: absolute;
+	top: -50px;
+	left: 50%;
+	margin-left: -32px;
+	transition: top left 0.6s back-out, opacity 0.4s, image-color 0.4s 0.3s quadratic-out;
+	opacity: 0;
+	image-color: #fff;
+}
+#transition:hover > img
+{
+	top: 50px;
+	opacity: 1;
+	image-color: #f61;
+}
+#transition:hover .alien1 { left: 30%; top: 75px; }
+#transition:hover .alien3 { left: 70%; top: 75px; }
+#transition .defender
+{ 
+	transition: image-color 0.3s 0.0s quadratic-out;
+	position: absolute;
+	left: -44px;
+	top: 150px; opacity: 1; 
+}
+#transition:hover .defender { image-color: #acf;  }
+#transition .ray { 
+	transition: top 0.4s back-out, opacity 0.4s cubic-in;
+	position: absolute;
+	top: -130px;
+	left: 50%;
+	opacity: 0;
+	margin-left: -20px;
+	width: 40px;
+	height: 200px;
+	decorator: gradient( vertical #daf0 #fef6 );
+}
+#transition:hover .ray
+{
+	opacity: 1;
+	top: 0px;
+}
+
+@keyframes cube-rotate {
+	from { transform: rotate3d(1, -1, 0.3, 0deg);	}
+	to { transform: rotate3d(1, -1, 0.3, 360deg);	}
+}
+
+.cube_container {
+	width: 200px;
+	height: 200px;
+	margin: 20px auto;
+	perspective: 500px;
+}
+.cube {
+	width: 100%;
+	height: 100%;
+	position: relative;
+	animation: 3s cube-rotate infinite back-in-out;
+}
+.face {
+	left: 50px; top: 50px;
+	display: block;
+	position: absolute;
+	width: 100px;
+	height: 100px;
+	line-height: 100px;
+	font-size: 60px;
+	color: white;
+	text-align: center;
+}
+/* Define each face based on direction */
+.face.front {
+	background: rgba(0, 0, 0, 160);
+	transform: translateZ(50px);
+}
+.face.front:hover {
+	background: rgba(255, 255, 0, 120);
+}
+.face.back {
+	background: rgba(0, 255, 0, 255);
+	color: black;
+	transform: rotateY(180deg) translateZ(50px);
+}
+.face.right {
+	background: rgba(196, 0, 0, 200);
+	transform: rotateY(90deg) translateZ(50px);
+}
+.face.left {
+	background: rgba(0, 0, 196, 200);
+	transform: rotateY(-90deg) translateZ(50px);
+}
+.face.top {
+	background: rgba(196, 196, 0, 200);
+	transform: rotateX(90deg) translateZ(50px);
+}
+.face.bottom {
+	background: rgba(196, 0, 196, 200);
+	transform: rotateX(-90deg) translateZ(50px);
+}
+
+
+/***  Forms  ***/
+
 form
 {
 	display: block;
@@ -204,6 +349,9 @@ form h2
 	color: #ddd;
 }
 
+
+/***  Sandbox  ***/
+
 #sandbox
 {
 	position: absolute;
@@ -293,21 +441,71 @@ form h2
 		<div class="transform fit-scale-none"></div>
 		<div class="transform fit-scale-down"></div>
 	</div>
-</panel>
-<tab>Buttons</tab>
-<panel class="minipanel">
-	<button id="start_game">Start Game</button><br />
-	<button id="high_scores" autofocus>High Scores</button><br />
-	<button id="options">Options</button><br />
-	<button id="help">Help</button><br />
-	<div><button id="exit" onclick="exit">Exit</button></div>
-	<img src="../../../assets/high_scores_defender.tga"/>
+	
+	<h1>Image elements</h1>
+	<img src="../../../assets/high_scores_defender.tga" class="clickable" onclick="change_color"/>
 	<img sprite="icon-game" style="vertical-align: 10px;"/>
 	<img src="../../../assets/high_scores_defender.tga" style="image-color: #fc5;" rect="0 0 64 64"/>
 	<img src="../../../assets/high_scores_defender.tga" style="image-color: #9c5;" rect="64 0 64 64"/>
-	<p>Img elements can take both images and sprites. For images it also supports a 'rect' attribute for specifying a sub-rectangle.</p>
+	<p style="text-align: left;">While they are described here, `img` elements are separate from decorators. They can be declared as in html. Additionally, a 'rect' attribute can be used to crop the image. In addition to images, the element can also display sprites declared in a sprite sheet.</p>
+</panel>
+<tab>Animations</tab>
+<panel id="animations" class="minipanel">
+	
+	<h1>Transitions</h1>
+	<p>Hover over the following to see an animation performed by the transition property in RCSS.</p>
+	<div id="transition">
+		<img class="alien alien1" src="../../../assets/high_scores_alien_1.tga"/>
+		<img class="alien alien2" src="../../../assets/high_scores_alien_2.tga"/>
+		<img class="alien alien3" src="../../../assets/high_scores_alien_3.tga"/>
+		<div class="ray"><img class="defender" src="../../../assets/high_scores_defender.tga"/></div>
+		<div style="height: 110%;"/>
+	</div>
+	
+	<h1>Tweening functions</h1>
+	<p>Set animation parameters below and click on the background or the flag.</p>
+	<div id="tweening_area" onclick="move_child">
+		<img sprite="icon-flag" onclick="change_color"/>
+	</div>
+	<select name="tween_function" onchange="tween_function">
+		<option value="linear" selected>Tweening function</option>
+		<option value="back">Back</option>
+		<option value="bounce">Bounce</option>
+		<option value="circular">Circular</option>
+		<option value="cubic">Cubic</option>
+		<option value="elastic">Elastic</option>
+		<option value="exponential">Exponential</option>
+		<option value="linear">Linear</option>
+		<option value="quadratic">Quadratic</option>
+		<option value="quartic">Quartic</option>
+		<option value="quintic">Quintic</option>
+		<option value="sine">Sine</option>
+	</select>
+	<select name="tween_direction" onchange="tween_direction">
+		<option value="out" selected>Direction</option>
+		<option value="in">In</option>
+		<option value="out">Out</option>
+		<option value="in-out">In-Out</option>
+	</select>
+	<div>
+		<input type="range" style="width: 150px; margin-right: 1em;" min="0" max="2" step="0.05" value="0.5" onchange="tween_duration"/> 
+		Duration <span id="duration">0.50</span> s
+	</div>
+	
+	<h1>Cube</h1>
+	<p>The cube is transformed and animated entirely in RCSS using the @keyframes at-rule.</p>
+	<div class="cube_container">
+		<div class="cube">
+			<div class="face back">6</div>
+			<div class="face top">5</div>
+			<div class="face left">4</div>
+			<div class="face bottom">3</div>
+			<div class="face right">2</div>
+			<div class="face front">1</div>
+		</div>
+	</div>
 </panel>
-<tab>Controls</tab>
+<tab>Forms</tab>
 <panel id="controls" class="minipanel">
 	<form onsubmit="submit_form">
 		<h2>Full name</h2>
@@ -334,7 +532,7 @@ form h2
 		</div>
 		<h2>Rating</h2>
 		<div>
-			<input type="range" name="rating" min="0" max="100" step="1" value="50" onchange="rating" onshow="rating"/> <span id="rating"/><span id="rating_emoji">&nbsp;</span>
+			<input type="range" name="rating" min="0" max="100" step="1" value="50" onchange="rating"/> <span id="rating"/><span id="rating_emoji">&nbsp;</span>
 		</div>
 		<h2>Subject</h2>
 		<div>

+ 67 - 4
Samples/basic/demo/src/main.cpp

@@ -192,6 +192,13 @@ Rml::Core::Context* context = nullptr;
 ShellRenderInterfaceExtensions *shell_renderer;
 std::unique_ptr<DemoWindow> demo_window;
 
+struct TweeningParameters {
+	Rml::Core::Tween::Type type = Rml::Core::Tween::Linear;
+	Rml::Core::Tween::Direction direction = Rml::Core::Tween::Out;
+	float duration = 0.5f;
+} tweening_parameters;
+
+
 void GameLoop()
 {
 	context->Update();
@@ -219,9 +226,9 @@ public:
 			// Test replacing the current element.
 			// Need to be careful with regard to lifetime issues. The event's current element will be destroyed, so we cannot
 			// use it after SetInnerRml(). The library should handle this case safely internally when propagating the event further.
-			auto parent = element->GetParentNode();
+			Element* parent = element->GetParentNode();
 			parent->SetInnerRML("<button onclick='confirm_exit' onblur='cancel_exit' onmouseout='cancel_exit'>Are you sure?</button>");
-			if (auto child = parent->GetChild(0))
+			if (Element* child = parent->GetChild(0))
 				child->Focus();
 		}
 		else if (value == "confirm_exit")
@@ -230,9 +237,65 @@ public:
 		}
 		else if (value == "cancel_exit")
 		{
-			if(auto parent = element->GetParentNode())
+			if(Element* parent = element->GetParentNode())
 				parent->SetInnerRML("<button id='exit' onclick='exit'>Exit</button>");
 		}
+		else if (value == "change_color")
+		{
+			Colourb color(Math::RandomInteger(255), Math::RandomInteger(255), Math::RandomInteger(255));
+			element->Animate("image-color", Property(color, Property::COLOUR), tweening_parameters.duration, Tween(tweening_parameters.type, tweening_parameters.direction));
+			event.StopPropagation();
+		}
+		else if (value == "move_child")
+		{
+			Vector2f mouse_pos( event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f) );
+			if (Element* child = element->GetFirstChild())
+			{
+				Vector2f new_pos = mouse_pos - element->GetAbsoluteOffset() - Vector2f(0.35f * child->GetClientWidth(), 0.9f * child->GetClientHeight());
+				Property destination = Transform::MakeProperty({ Transforms::Translate2D(new_pos.x, new_pos.y) });
+				if(tweening_parameters.duration <= 0)
+					child->SetProperty(PropertyId::Transform, destination);
+				else
+					child->Animate("transform", destination, tweening_parameters.duration, Tween(tweening_parameters.type, tweening_parameters.direction));
+			}
+		}
+		else if (value == "tween_function")
+		{
+			static const SmallUnorderedMap<String, Tween::Type> tweening_functions = {
+				{"back", Tween::Back}, {"bounce", Tween::Bounce},
+				{"circular", Tween::Circular}, {"cubic", Tween::Cubic},
+				{"elastic", Tween::Elastic}, {"exponential", Tween::Exponential},
+				{"linear", Tween::Linear}, {"quadratic", Tween::Quadratic},
+				{"quartic", Tween::Quartic}, {"quintic", Tween::Quintic},
+				{"sine", Tween::Sine}
+			};
+
+			String value = event.GetParameter("value", String());
+			auto it = tweening_functions.find(value);
+			if (it != tweening_functions.end())
+				tweening_parameters.type = it->second;
+			else
+				RMLUI_ERROR;
+		}
+		else if (value == "tween_direction")
+		{
+			String value = event.GetParameter("value", String());
+			if (value == "in")
+				tweening_parameters.direction = Tween::In;
+			else if(value == "out")
+				tweening_parameters.direction = Tween::Out;
+			else if(value == "in-out")
+				tweening_parameters.direction = Tween::InOut;
+			else
+				RMLUI_ERROR;
+		}
+		else if (value == "tween_duration")
+		{
+			float value = std::atof(static_cast<Rml::Controls::ElementFormControl*>(element)->GetValue().c_str());
+			tweening_parameters.duration = value;
+			if (auto el_duration = element->GetElementById("duration"))
+				el_duration->SetInnerRML(CreateString(20, "%2.2f", value));
+		}
 		else if (value == "rating")
 		{
 			auto el_rating = element->GetElementById("rating");
@@ -261,7 +324,7 @@ public:
 		}
 		else if (value == "submit_form")
 		{
-			if (auto el_output = element->GetElementById("form_output"))
+			if (Element* el_output = element->GetElementById("form_output"))
 			{
 				const auto& p = event.GetParameters();
 				Rml::Core::String output = "<p>";

+ 4 - 0
Samples/shell/src/win32/ShellWin32.cpp

@@ -48,6 +48,7 @@ static std::unique_ptr<ShellFileInterface> file_interface;
 
 static HCURSOR cursor_default = nullptr;
 static HCURSOR cursor_move = nullptr;
+static HCURSOR cursor_pointer = nullptr;
 static HCURSOR cursor_resize= nullptr;
 static HCURSOR cursor_cross = nullptr;
 static HCURSOR cursor_text = nullptr;
@@ -68,6 +69,7 @@ bool Shell::Initialise()
 	// Load cursors
 	cursor_default = LoadCursor(nullptr, IDC_ARROW);
 	cursor_move = LoadCursor(nullptr, IDC_SIZEALL);
+	cursor_pointer = LoadCursor(nullptr, IDC_HAND);
 	cursor_resize = LoadCursor(nullptr, IDC_SIZENWSE);
 	cursor_cross = LoadCursor(nullptr, IDC_CROSS);
 	cursor_text = LoadCursor(nullptr, IDC_IBEAM);
@@ -286,6 +288,8 @@ void Shell::SetMouseCursor(const Rml::Core::String& cursor_name)
 			cursor_handle = cursor_default;
 		else if(cursor_name == "move")
 			cursor_handle = cursor_move;
+		else if (cursor_name == "pointer")
+			cursor_handle = cursor_pointer;
 		else if (cursor_name == "resize")
 			cursor_handle = cursor_resize;
 		else if (cursor_name == "cross")

+ 2 - 3
Source/Controls/InputTypeRange.cpp

@@ -67,11 +67,10 @@ bool InputTypeRange::OnAttributeChange(const Core::ElementAttributes& changed_at
 	bool dirty_layout = false;
 
 	// Check if maxlength has been defined.
-	static const Core::String str_orientation = "orientation";
-	auto it_orientation = changed_attributes.find(str_orientation);
+	auto it_orientation = changed_attributes.find("orientation");
 	if (it_orientation != changed_attributes.end())
 	{
-		bool is_vertical = (it_orientation->second.Get<Rml::Core::String>(str_orientation) == "vertical");
+		bool is_vertical = (it_orientation->second.Get<Rml::Core::String>() == "vertical");
 		widget->SetOrientation(is_vertical ? WidgetSliderInput::VERTICAL : WidgetSliderInput::HORIZONTAL);
 		dirty_layout = true;
 	}