Browse Source

Support special keyword and defaults in flex shorthand property

Michael Ragazzon 4 years ago
parent
commit
606aa37176

+ 3 - 1
Include/RmlUi/Core/PropertySpecification.h

@@ -55,7 +55,9 @@ enum class ShorthandType
 	// Repeatedly resolves the full value string on each property, whether it is a normal property or another shorthand.
 	// Repeatedly resolves the full value string on each property, whether it is a normal property or another shorthand.
 	RecursiveRepeat,
 	RecursiveRepeat,
 	// Comma-separated list of properties or shorthands, the number of declared values must match the specified.
 	// Comma-separated list of properties or shorthands, the number of declared values must match the specified.
-	RecursiveCommaSeparated
+	RecursiveCommaSeparated,
+	// The 'flex' shorthand has some special behavior but otherwise acts like 'FallThrough'.
+	Flex
 };
 };
 
 
 
 

+ 29 - 1
Source/Core/PropertySpecification.cpp

@@ -270,6 +270,31 @@ bool PropertySpecification::ParseShorthandDeclaration(PropertyDictionary& dictio
 	if (!shorthand_definition)
 	if (!shorthand_definition)
 		return false;
 		return false;
 
 
+	// Handle the special behavior of the flex shorthand first, otherwise it acts like 'FallThrough'.
+	if (shorthand_definition->type == ShorthandType::Flex)
+	{
+		RMLUI_ASSERT(shorthand_definition->items.size() == 3);
+		if (!property_values.empty() && property_values[0] == "none")
+		{
+			property_values = {"0", "0", "auto"};
+		}
+		else
+		{
+			// Default values when omitted from the 'flex' shorthand is specified here. These defaults are special
+			// for this shorthand only, otherwise each underlying property has a different default value.
+			const char* default_omitted_values[] = {"1", "1", "0"}; // flex-grow, flex-shrink, flex-basis
+			Property new_property;
+			bool result = true;
+			for (int i = 0; i < 3; i++)
+			{
+				auto& item = shorthand_definition->items[i];
+				result &= item.property_definition->ParseValue(new_property, default_omitted_values[i]);
+				dictionary.SetProperty(item.property_id, new_property);
+			}
+			RMLUI_ASSERT(result);
+		}
+	}
+
 	// If this definition is a 'box'-style shorthand (x-top, x-right, x-bottom, x-left, etc) and there are fewer
 	// If this definition is a 'box'-style shorthand (x-top, x-right, x-bottom, x-left, etc) and there are fewer
 	// than four values
 	// than four values
 	if (shorthand_definition->type == ShorthandType::Box &&
 	if (shorthand_definition->type == ShorthandType::Box &&
@@ -362,6 +387,9 @@ bool PropertySpecification::ParseShorthandDeclaration(PropertyDictionary& dictio
 	}
 	}
 	else
 	else
 	{
 	{
+		RMLUI_ASSERT(shorthand_definition->type == ShorthandType::Box || shorthand_definition->type == ShorthandType::FallThrough ||
+			shorthand_definition->type == ShorthandType::Replicate || shorthand_definition->type == ShorthandType::Flex);
+
 		size_t value_index = 0;
 		size_t value_index = 0;
 		size_t property_index = 0;
 		size_t property_index = 0;
 
 
@@ -373,7 +401,7 @@ bool PropertySpecification::ParseShorthandDeclaration(PropertyDictionary& dictio
 			{
 			{
 				// This definition failed to parse; if we're falling through, try the next property. If there is no
 				// This definition failed to parse; if we're falling through, try the next property. If there is no
 				// next property, then abort!
 				// next property, then abort!
-				if (shorthand_definition->type == ShorthandType::FallThrough)
+				if (shorthand_definition->type == ShorthandType::FallThrough || shorthand_definition->type == ShorthandType::Flex)
 				{
 				{
 					if (property_index + 1 < shorthand_definition->items.size())
 					if (property_index + 1 < shorthand_definition->items.size())
 						continue;
 						continue;

+ 1 - 1
Source/Core/StyleSheetSpecification.cpp

@@ -435,7 +435,7 @@ void StyleSheetSpecification::RegisterDefaultProperties()
 	RegisterProperty(PropertyId::FlexWrap, "flex-wrap", "nowrap", false, true).AddParser("keyword", "nowrap, wrap, wrap-reverse");
 	RegisterProperty(PropertyId::FlexWrap, "flex-wrap", "nowrap", false, true).AddParser("keyword", "nowrap, wrap, wrap-reverse");
 	RegisterProperty(PropertyId::JustifyContent, "justify-content", "flex-start", false, true).AddParser("keyword", "flex-start, flex-end, center, space-between, space-around");
 	RegisterProperty(PropertyId::JustifyContent, "justify-content", "flex-start", false, true).AddParser("keyword", "flex-start, flex-end, center, space-between, space-around");
 
 
-	RegisterShorthand(ShorthandId::Flex, "flex", "flex-grow, flex-shrink, flex-basis", ShorthandType::FallThrough); // Todo: 'none' keyword
+	RegisterShorthand(ShorthandId::Flex, "flex", "flex-grow, flex-shrink, flex-basis", ShorthandType::Flex);
 	RegisterShorthand(ShorthandId::FlexFlow, "flex-flow", "flex-direction, flex-wrap", ShorthandType::FallThrough);
 	RegisterShorthand(ShorthandId::FlexFlow, "flex-flow", "flex-direction, flex-wrap", ShorthandType::FallThrough);
 
 
 	RMLUI_ASSERTMSG(instance->properties.shorthand_map->AssertAllInserted(ShorthandId::NumDefinedIds), "Missing specification for one or more Shorthand IDs.");
 	RMLUI_ASSERTMSG(instance->properties.shorthand_map->AssertAllInserted(ShorthandId::NumDefinedIds), "Missing specification for one or more Shorthand IDs.");

+ 92 - 0
Tests/Source/UnitTests/Properties.cpp

@@ -0,0 +1,92 @@
+/*
+ * 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 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 "../Common/TestsInterface.h"
+#include <RmlUi/Core/Context.h>
+#include <RmlUi/Core/Core.h>
+#include <RmlUi/Core/Element.h>
+#include <RmlUi/Core/ElementDocument.h>
+#include <doctest.h>
+
+using namespace Rml;
+
+TEST_CASE("Properties")
+{
+	const Vector2i window_size(1024, 768);
+
+	TestsSystemInterface system_interface;
+	TestsRenderInterface render_interface;
+
+	SetRenderInterface(&render_interface);
+	SetSystemInterface(&system_interface);
+
+	Rml::Initialise();
+
+	Context* context = Rml::CreateContext("main", window_size);
+	ElementDocument* document = context->CreateDocument();
+
+	struct FlexTestCase {
+		String flex_value;
+
+		struct ExpectedValues {
+			float flex_grow;
+			float flex_shrink;
+			String flex_basis;
+		} expected;
+	};
+
+	FlexTestCase tests[] = {
+		{"", {0.f, 1.f, "auto"}},
+		{"none", {0.f, 0.f, "auto"}},
+		{"auto", {1.f, 1.f, "auto"}},
+		{"1", {1.f, 1.f, "0px"}},
+		{"2", {2.f, 1.f, "0px"}},
+		{"2 0", {2.f, 0.f, "0px"}},
+		{"2 3", {2.f, 3.f, "0px"}},
+		{"2 auto", {2.f, 1.f, "auto"}},
+		{"2 0 auto", {2.f, 0.f, "auto"}},
+		{"0 0 auto", {0.f, 0.f, "auto"}},
+		{"0 0 50px", {0.f, 0.f, "50px"}},
+		{"0 0 50px", {0.f, 0.f, "50px"}},
+		{"0 0 0", {0.f, 0.f, "0px"}},
+	};
+
+	for (const FlexTestCase& test : tests)
+	{
+		if (!test.flex_value.empty())
+		{
+			CHECK(document->SetProperty("flex", test.flex_value));
+		}
+
+		CHECK(document->GetProperty<float>("flex-grow") == test.expected.flex_grow);
+		CHECK(document->GetProperty<float>("flex-shrink") == test.expected.flex_shrink);
+		CHECK(document->GetProperty("flex-basis")->ToString() == test.expected.flex_basis);
+	}
+
+	Rml::Shutdown();
+}