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 <[email protected]>
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;

Reply via email to