You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2026/07/01 11:53:03 UTC

(royale-asjs) branch develop updated: Support for named colors/custom palettes

This is an automated email from the ASF dual-hosted git repository.

greg-dove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git


The following commit(s) were added to refs/heads/develop by this push:
     new 4a72e78feb Support for named colors/custom palettes
4a72e78feb is described below

commit 4a72e78febad573ea3d72f06058e4859b24aa290
Author: greg-dove <gr...@gmail.com>
AuthorDate: Wed Jul 1 13:52:21 2026 +0200

    Support for named colors/custom palettes
---
 .../Style/src/main/resources/style-manifest.xml    |   1 +
 .../projects/Style/src/main/royale/StyleClasses.as |   2 +
 .../org/apache/royale/style/colors/ColorSwatch.as  |  27 ++-
 .../apache/royale/style/colors/NamedColorSet.as    | 216 +++++++++++++++++++++
 .../royale/style/stylebeads/StyleBeadBase.as       |   6 +-
 5 files changed, 250 insertions(+), 2 deletions(-)

diff --git a/frameworks/projects/Style/src/main/resources/style-manifest.xml b/frameworks/projects/Style/src/main/resources/style-manifest.xml
index 407135f4da..da8d5eca4a 100644
--- a/frameworks/projects/Style/src/main/resources/style-manifest.xml
+++ b/frameworks/projects/Style/src/main/resources/style-manifest.xml
@@ -79,6 +79,7 @@
   <component id="FlexLayout" class="org.apache.royale.style.beads.FlexLayout"/>
   <component id="Link" class="org.apache.royale.style.Link"/>
   <component id="HoverState" class="org.apache.royale.style.stylebeads.states.HoverState"/>
+  <component id="ActiveState" class="org.apache.royale.style.stylebeads.states.ActiveState"/>
   <component id="Toast" class="org.apache.royale.style.Toast"/>
   <component id="Tooltip" class="org.apache.royale.style.Tooltip"/>
   <component id="BarLoader" class="org.apache.royale.style.BarLoader"/>
diff --git a/frameworks/projects/Style/src/main/royale/StyleClasses.as b/frameworks/projects/Style/src/main/royale/StyleClasses.as
index 1200cc2588..7b35e2416f 100644
--- a/frameworks/projects/Style/src/main/royale/StyleClasses.as
+++ b/frameworks/projects/Style/src/main/royale/StyleClasses.as
@@ -28,6 +28,7 @@ package
 	internal class StyleClasses
 	{
 		import org.apache.royale.style.colors.ColorSwatch; ColorSwatch;
+		import org.apache.royale.style.colors.NamedColorSet; NamedColorSet;
 		import org.apache.royale.style.util.StyleManager; StyleManager;
 		import org.apache.royale.style.util.ThemeManager; ThemeManager;
 		import org.apache.royale.style.util.ContentAlign; ContentAlign;
@@ -45,6 +46,7 @@ package
 		import org.apache.royale.style.stylebeads.states.media.MediaBreakpoint; MediaBreakpoint;
 		import org.apache.royale.style.stylebeads.states.media.MediaBetween; MediaBetween;
 		import org.apache.royale.style.stylebeads.states.media.ContainerBreakpoint; ContainerBreakpoint;
+		import org.apache.royale.style.stylebeads.states.ActiveState; ActiveState;
 		
 	}
 
diff --git a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/ColorSwatch.as b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/ColorSwatch.as
index 83d2c33baf..221d903a52 100644
--- a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/ColorSwatch.as
+++ b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/ColorSwatch.as
@@ -206,8 +206,8 @@ package org.apache.royale.style.colors
 		
 		public static function fromSpecifier(specifier:String,darkMode:Boolean=false):ColorSwatch
 		{
+			assert(isSpecifier(specifier), "Invalid color specifier: " + specifier);
 			var parts:Array = specifier.split("-");
-			assert(parts.length == 2, "Invalid color specifier: " + specifier);
 			var base:String = parts[0];
 			var shadeAndOpacity:String = parts[1];
 			var shadeParts:Array = shadeAndOpacity.split("/");
@@ -215,6 +215,31 @@ package org.apache.royale.style.colors
 			var opacity:Number = shadeParts.length > 1 ? Number(shadeParts[1]) : 100;
 			return new ColorSwatch(base, shade, opacity, darkMode);
 		}
+
+		public static function isSpecifier(specifier:String):Boolean
+		{
+			if (!specifier)
+				return false;
+			var parts:Array = specifier.split("-");
+			if (parts.length != 2)
+				return false;
+			var base:String = parts[0];
+			if (!(isColorName(base) || base == "black" || base == "white" || CSSLookup.has(base)))
+				return false;
+			var shadeParts:Array = parts[1].split("/");
+			if (shadeParts.length > 2)
+				return false;
+			var shade:Number = Number(shadeParts[0]);
+			if (isNaN(shade) || shade < 0 || shade > 1000)
+				return false;
+			if (shadeParts.length == 2)
+			{
+				var opacity:Number = Number(shadeParts[1]);
+				if (isNaN(opacity) || opacity < 0 || opacity > 100)
+					return false;
+			}
+			return true;
+		}
 		
 		public static function isColorName(name:String):Boolean{
 			return name in BASE_COLORS;
diff --git a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/NamedColorSet.as b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/NamedColorSet.as
new file mode 100644
index 0000000000..3839b916aa
--- /dev/null
+++ b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/colors/NamedColorSet.as
@@ -0,0 +1,216 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.style.colors
+{
+	import org.apache.royale.debugging.assert;
+	import org.apache.royale.style.util.CSSLookup;
+
+	public class NamedColorSet
+	{
+		private static const OPACITY_SEPARATOR:String = "/";
+		private static const HEX_COLOR:RegExp = /^#?([0-9a-fA-F]{6})$/;
+		private static const HEX_COLOR_WITH_PREFIX:RegExp = /^0x([0-9a-fA-F]{6})$/;
+		private static const WHITESPACE:RegExp = /\s/;
+
+		private static const colors:Object = {};
+
+		private function NamedColorSet()
+		{
+			assert(false, "NamedColorSet should not be instantiated.");
+		}
+
+		/**
+		 * Registers an exact named color and returns the registered name.
+		 * This is useful from application palette static getters.
+		 */
+		public static function register(name:String, value:*):String
+		{
+			validateName(name);
+			var entry:Object = createEntry(value);
+			var existing:Object = colors[name];
+			if (existing)
+			{
+				assert(existing.rule == entry.rule, "Named color '" + name + "' is already registered with a different value.");
+				return name;
+			}
+			colors[name] = entry;
+			CSSLookup.register(name, entry.rule);
+			return name;
+		}
+
+		/**
+		 * Registers a named color and returns either its base name or an opacity variant specifier.
+		 */
+		public static function registerWithOpacity(name:String, value:*, opacity:Number = 100):String
+		{
+			register(name, value);
+			return withOpacity(name, opacity);
+		}
+
+		/**
+		 * Returns an opacity variant specifier, registering its CSS value on first use.
+		 */
+		public static function withOpacity(name:String, opacity:Number):String
+		{
+			validateOpacity(opacity);
+			var entry:Object = colors[name];
+			assert(entry, "Unknown named color: " + name);
+			if (opacity == 100)
+				return name;
+			var specifier:String = getSpecifier(name, opacity);
+			if (!CSSLookup.has(specifier))
+			{
+				CSSLookup.register(specifier, getRule(entry, opacity));
+			}
+			return specifier;
+		}
+
+		public static function has(name:String):Boolean
+		{
+			if (!name)
+				return false;
+			if (colors[name])
+				return true;
+			if (CSSLookup.has(name))
+				return true;
+			var parsed:Object = parseSpecifier(name);
+			return parsed && colors[parsed.name] != null;
+		}
+
+		/**
+		 * Resolves a named color or opacity variant to a CSS color value.
+		 * If an opacity variant has not been requested before, it is registered on demand.
+		 */
+		public static function resolve(name:String):String
+		{
+			if (!name)
+				return null;
+			var entry:Object = colors[name];
+			if (entry)
+				return entry.rule;
+			if (CSSLookup.has(name))
+				return CSSLookup.getProperty(name);
+			var parsed:Object = parseSpecifier(name);
+			if (!parsed)
+				return null;
+			entry = colors[parsed.name];
+			if (!entry)
+				return null;
+			withOpacity(parsed.name, parsed.opacity);
+			return getRule(entry, parsed.opacity);
+		}
+
+		private static function validateName(name:String):void
+		{
+			assert(name && name.length > 0, "Named color name must not be empty.");
+			assert(name.indexOf("--") != 0, "Named color names must not start with '--': " + name);
+			assert(name.indexOf(OPACITY_SEPARATOR) == -1, "Named color names must not contain '" + OPACITY_SEPARATOR + "': " + name);
+			assert(!WHITESPACE.test(name), "Named color names must not contain whitespace: " + name);
+		}
+
+		private static function validateOpacity(opacity:Number):void
+		{
+			assert(isValidOpacity(opacity), "Opacity must be between 0 and 100.");
+		}
+
+		private static function isValidOpacity(opacity:Number):Boolean
+		{
+			return !isNaN(opacity) && opacity >= 0 && opacity <= 100;
+		}
+
+		private static function createEntry(value:*):Object
+		{
+			var rgb:Array = getRGB(value);
+			if (rgb)
+			{
+				return {
+					rule: CSSColor.getColor(rgb),
+					rgb: rgb
+				};
+			}
+			var rule:String = String(value);
+			assert(rule && rule.length > 0, "Named color value must not be empty.");
+			return {
+				rule: rule,
+				rgb: null
+			};
+		}
+
+		private static function getRGB(value:*):Array
+		{
+			var color:uint;
+			if (typeof value == "number")
+			{
+				assert(!isNaN(Number(value)), "Named color numeric value must be a valid 24-bit RGB color.");
+				color = uint(value);
+				assert(color <= 0xFFFFFF, "Named color numeric value must be a 24-bit RGB color.");
+				return uintToRGB(color);
+			}
+			if (value is String)
+			{
+				var stringValue:String = String(value);
+				var match:Object = HEX_COLOR.exec(stringValue);
+				if (!match)
+					match = HEX_COLOR_WITH_PREFIX.exec(stringValue);
+				if (match)
+				{
+					color = uint(parseInt(match[1], 16));
+					return uintToRGB(color);
+				}
+			}
+			return null;
+		}
+
+		private static function uintToRGB(value:uint):Array
+		{
+			return [
+				(value >> 16) & 0xff,
+				(value >> 8) & 0xff,
+				value & 0xff
+			];
+		}
+
+		private static function getSpecifier(name:String, opacity:Number):String
+		{
+			return name + OPACITY_SEPARATOR + opacity;
+		}
+
+		private static function getRule(entry:Object, opacity:Number):String
+		{
+			if (opacity == 100)
+				return entry.rule;
+			assert(entry.rgb, "Opacity variants require a 6-digit RGB color value.");
+			return CSSColor.getColor(entry.rgb, opacity);
+		}
+
+		private static function parseSpecifier(value:String):Object
+		{
+			var parts:Array = value.split(OPACITY_SEPARATOR);
+			if (parts.length != 2)
+				return null;
+			var opacity:Number = Number(parts[1]);
+			if (!isValidOpacity(opacity))
+				return null;
+			return {
+				name: parts[0],
+				opacity: opacity
+			};
+		}
+	}
+}
diff --git a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/StyleBeadBase.as b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/StyleBeadBase.as
index 1b9a80ec98..15308739c6 100644
--- a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/StyleBeadBase.as
+++ b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/StyleBeadBase.as
@@ -25,6 +25,7 @@ package org.apache.royale.style.stylebeads
 	import org.apache.royale.style.util.CSSLookup;
 	import org.apache.royale.style.util.StyleData;
 	import org.apache.royale.style.colors.ColorSwatch;
+	import org.apache.royale.style.colors.NamedColorSet;
 	import org.apache.royale.utils.StringUtil;
 	
 	[DefaultProperty("styles")]
@@ -197,7 +198,10 @@ package org.apache.royale.style.stylebeads
 				default:
 					if(CSSLookup.has(color))
 						return CSSLookup.getProperty(color);
-					else if (color.indexOf("-") != -1)
+					var namedColor:String = NamedColorSet.resolve(color);
+					if(namedColor)
+						return namedColor;
+					if (ColorSwatch.isSpecifier(color))
 					{
 						var swatch:ColorSwatch = ColorSwatch.fromSpecifier(color, false);
 						return swatch.colorValue;