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;