Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
63a6e38
CSSThemeCompiler: #Constants, font-size and box semantics matching th…
shai-almog Oct 15, 2026
821dc2c
CSSThemeCompiler: color and image constants, state fonts inherit the …
shai-almog Oct 16, 2026
bc3cbf4
CSSThemeCompiler: constants in source order, var() color constants, d…
shai-almog Oct 16, 2026
51a606e
CSSThemeCompiler: resolve var() before looking up an image constant
shai-almog Oct 16, 2026
3435ab5
CSSThemeCompiler: defer image constants past rules, quote-aware rule …
shai-almog Oct 17, 2026
6df549f
CSSThemeCompiler: rgba() and quoted constants, quote-aware comments, …
shai-almog Oct 17, 2026
5a719cf
CSSThemeCompiler: every rule resolves var() against the final constants
shai-almog Oct 17, 2026
2abd19c
CSSThemeCompiler: cn1rgb constants, literal aliases, state derives in…
shai-almog Oct 17, 2026
8f097f3
CSSThemeCompiler: match the build-time compiler on ordering, * fonts,…
shai-almog Oct 17, 2026
24a44eb
CSSThemeCompiler: per-compile constant typing, sibling-state derives,…
shai-almog Oct 17, 2026
8e55384
CSSThemeCompiler: in-rule custom property order, last state derive wi…
shai-almog Oct 17, 2026
6c955f3
CSSThemeCompiler: earlier compiles' constants stay text, same-state r…
shai-almog Oct 17, 2026
a58fdb4
CSSThemeCompiler: strip !important, keep system font families and ear…
shai-almog Oct 17, 2026
0fd2f0d
CSSThemeCompiler: a quoted custom property stays a string token in st…
shai-almog Oct 17, 2026
b70aedb
CSSThemeCompiler: last derive wins for every state, build-time family…
shai-almog Oct 18, 2026
25236ab
CSSThemeCompiler: #Constants aliases resolve where declared
shai-almog Oct 18, 2026
81cdb2c
CSSThemeCompiler: #Constants in source order, var() fallbacks; CSSThe…
shai-almog Oct 18, 2026
42597d5
CSSThemeCompiler: track the open quote for !important, reject #Consta…
shai-almog Oct 18, 2026
af0590d
CSSThemeCompiler: #Constants stores numbers as the build does, reject…
shai-almog Oct 18, 2026
507d975
CSSThemeCompiler: keyword size keeps an inherited native family, font…
shai-almog Oct 18, 2026
87578bd
CSSThemeCompiler: drop constants with an undefined var(), reject unte…
shai-almog Oct 18, 2026
c3768fb
CSSThemeCompiler: only --name declarations are variables, dark fonts …
shai-almog Oct 18, 2026
d9b8f3d
CSSThemeCompiler: a color or number named ...Image is not an image; i…
shai-almog Oct 18, 2026
e831d78
CSSThemeCompiler: #Constants aliases resolve against earlier declarat…
shai-almog Oct 18, 2026
16e04f9
CSSThemeCompiler: a plain #Constants entry no longer hides a custom p…
shai-almog Oct 19, 2026
df69ee6
CSSThemeCompiler: a later :root property does not change an exported …
shai-almog Oct 19, 2026
e1200fb
CSSThemeCompiler: variables kept apart from exported constants, multi…
shai-almog Oct 19, 2026
e45c42d
CSSThemeCompiler: constant names must be identifiers, image aliases d…
shai-almog Oct 19, 2026
43f32fd
CSSThemeCompiler: a direct multi-token constant exports its leading t…
shai-almog Oct 19, 2026
e5c2eda
CSSThemeCompiler: an image alias of a color or number is typed as one…
shai-almog Oct 19, 2026
313a87d
GUI builder: cache canvas support per declaration value, not per prop…
shai-almog Oct 19, 2026
63b9e91
CSSThemeCompiler: legacy @constants stay overridable, self-referencin…
shai-almog Oct 19, 2026
b0d26b2
CSSThemeCompiler: image constants look up their leading token; escape…
shai-almog Oct 19, 2026
02bfc37
CSSThemeCompiler: quoted image names survive alias chains, escaped se…
shai-almog Oct 19, 2026
450f107
CSSThemeCompiler: an escaped quote outside a string opens no string
shai-almog Oct 19, 2026
1732c45
CSSThemeCompiler: dark font chains read only dark derives, #Constants…
shai-almog Oct 19, 2026
58543d7
CSSThemeCompiler: escaped var() names decoded, --digit custom propert…
shai-almog Oct 19, 2026
c70b2e1
CSSThemeCompiler: a pressed-only derive inherits the target's normal …
shai-almog Oct 20, 2026
3f2dd3e
CSSThemeCompiler: escaped alias targets decoded for type lookup; a da…
shai-almog Oct 20, 2026
64d6855
CSSThemeCompiler: legacy @constants aliases stay live until the final…
shai-almog Oct 20, 2026
259eb51
CSSThemeCompiler: quoted var() fallbacks stay text, escaped custom pr…
shai-almog Oct 20, 2026
91cc7f3
CSSThemeCompiler: universal rules set no style property; a functional…
shai-almog Oct 20, 2026
699f677
CSSThemeCompiler: a pressed derive wins over the owner's, a size-only…
shai-almog Oct 20, 2026
82d95d8
CSSThemeCompiler: only CSS whitespace ends a hex escape inside a string
shai-almog Oct 20, 2026
daee10e
CSSThemeCompiler: a universal derive is ignored; a quoted image varia…
shai-almog Oct 20, 2026
07e0d66
CSSThemeCompiler: quoted image fallbacks keep the whole name, image-n…
shai-almog Oct 21, 2026
617245d
CSSThemeCompiler: escaped commas stay in a family name, an unsupporte…
shai-almog Oct 21, 2026
83bef32
Merge remote-tracking branch 'origin/master' into css-theme-compiler-…
shai-almog Oct 21, 2026
1e4c9c3
CSSThemeCompiler: nested quoted fallbacks stay text, an image constan…
shai-almog Oct 21, 2026
269adfb
CSSThemeCompiler: a UIID's font reaches its selected, pressed and dis…
shai-almog Oct 21, 2026
acfbbe9
CSSThemeCompiler: an alias of a quoted constant keeps its text, not r…
shai-almog Oct 21, 2026
de92522
Merge remote-tracking branch 'origin/master' into css-theme-compiler-…
shai-almog Oct 21, 2026
121c849
Baselines: take #6059's repair of #5976's merged overlays (rows maste…
shai-almog Oct 22, 2026
bfd6654
CSSThemeCompiler: fold CSS keywords, property names, colors and media…
shai-almog Oct 22, 2026
588eee3
CSSThemeCompiler: carry unselected styles and derives into every stat…
shai-almog Oct 22, 2026
eb6822e
CSSThemeCompiler: prefix UIIDs for platform/density/device @media lik…
shai-almog Oct 22, 2026
47fe448
CSSThemeCompiler: write @cn1-bind keys for var() colors, accept --var…
shai-almog Oct 22, 2026
fd264e6
CSSThemeCompiler: carry only the bare rule's values into states, drop…
shai-almog Oct 22, 2026
5b88e8b
CSSThemeCompiler: compile .unselected as a layer over the bare rule, …
shai-almog Oct 22, 2026
f868e76
Baselines: calibrate linux binary size for the runtime CSS theme comp…
shai-almog Oct 22, 2026
c53c970
CSSThemeCompiler: use HashMap for the compile's own scratch maps
shai-almog Oct 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,451 changes: 2,321 additions & 130 deletions CodenameOne/src/com/codename1/ui/css/CSSThemeCompiler.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3243,7 +3243,26 @@ public Object createTestFont(int charWidth, int height) {

@Override
public Object createFont(int face, int style, int size) {
return new TestFont(defaultFont.charWidth, defaultFont.height);
TestFont font = new TestFont(defaultFont.charWidth, defaultFont.height);
font.face = face;
font.style = style;
font.size = size;
return font;
}

@Override
public int getStyle(Object nativeFont) {
return nativeFont instanceof TestFont ? ((TestFont) nativeFont).style : 0;
}

@Override
public int getFace(Object nativeFont) {
return nativeFont instanceof TestFont ? ((TestFont) nativeFont).face : 0;
}

@Override
public int getSize(Object nativeFont) {
return nativeFont instanceof TestFont ? ((TestFont) nativeFont).size : 0;
}

@Override
Expand Down Expand Up @@ -5637,6 +5656,10 @@ public String toString() {
public static final class TestFont {
final int charWidth;
final int height;
/// What a system font was created with; a real port reports these from its native font.
int face;
int style;
int size = com.codename1.ui.Font.SIZE_MEDIUM;

TestFont(int charWidth, int height) {
this.charWidth = charWidth;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3246,6 +3246,12 @@ public void updateResources() {
}
} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_INTEGER) {
res.setThemeProperty(themeName, "@"+constantKey, String.valueOf(((ScaledUnit)lu).getIntegerValue()));
} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_REAL) {
// A decimal such as `switchTrackScaleY: 1.5` was silently dropped, so the
// built theme fell back to the default the runtime compiler did not.
String real = String.valueOf(lu.getFloatValue());
res.setThemeProperty(themeName, "@"+constantKey,
real.endsWith(".0") ? real.substring(0, real.length() - 2) : real);
} else if (lu.getLexicalUnitType() == LexicalUnit.SAC_RGBCOLOR
|| (lu.getLexicalUnitType() == LexicalUnit.SAC_FUNCTION
&& ("rgb".equals(lu.getFunctionName())
Expand Down Expand Up @@ -6825,6 +6831,13 @@ public com.codename1.ui.Font getThemeFont(Map<String,LexicalUnit> styles) {
ttfSizeType = TTF_SIZE_TYPE_LARGE;
sysSize = Font.SIZE_LARGE;
break outer;
case "medium":
// Without this case `medium` fell through to the pixel branch
// and stored a zero-size font.
iFontSizeType = Font.SIZE_MEDIUM;
ttfSizeType = TTF_SIZE_TYPE_MEDIUM;
sysSize = Font.SIZE_MEDIUM;
break outer;

}
}
Expand Down Expand Up @@ -6953,6 +6966,8 @@ public com.codename1.ui.Font getThemeFont(Map<String,LexicalUnit> styles) {
case "arial" :
case "cursive" :
case "fantasy" :
// The platform's own UI font: a generic family, as in the runtime compiler.
case "system-ui" :
iFontFace = Font.FACE_SYSTEM;
break loop;
case "monospace" :
Expand Down Expand Up @@ -9348,6 +9363,21 @@ static String getARGBHexString(LexicalUnit color) {
return "#" + leftPad(Integer.toHexString(getColorInt(color)), 6) + leftPad(Integer.toHexString(getColorAlphaInt(color)), 2);
}

/// One `rgb()` channel: `50%` is half of 255, which getIntegerValue() read as 0, so every
/// percentage color came out black.
private static int rgbChannel(ScaledUnit channel) {
if (channel.getLexicalUnitType() == LexicalUnit.SAC_PERCENTAGE) {
return clampChannel(Math.round(channel.getFloatValue() * 255f / 100f));
}
Comment thread
shai-almog marked this conversation as resolved.
return clampChannel(channel.getIntegerValue());
}

/// Out of range is clamped, as CSS specifies: `200%` or `300` is 255, not a value `%02X`
/// would write as three digits, breaking the six-digit color.
private static int clampChannel(int value) {
return Math.max(0, Math.min(255, value));
}

static String getColorString(LexicalUnit color) {
return getColorString(color, false);
}
Expand Down Expand Up @@ -9388,9 +9418,9 @@ static String getColorString(LexicalUnit color, boolean premultiplied) {
blue = (ScaledUnit)blue.getNextLexicalUnit();
}
return String.format( "%02X%02X%02X",
red.getIntegerValue(),
green.getIntegerValue(),
blue.getIntegerValue());
rgbChannel(red),
rgbChannel(green),
rgbChannel(blue));

}
case LexicalUnit.SAC_FUNCTION: {
Expand Down Expand Up @@ -9435,9 +9465,9 @@ static String getColorString(LexicalUnit color, boolean premultiplied) {
throw new RuntimeException("Failed to parse color " + color + ". Received null alpha parameter:" + color);
}
}
int r = (int)(red.getIntegerValue() * (premultiplied?(1-alpha.getNumericValue()):1));
int g = (int)(green.getIntegerValue() * (premultiplied?(1-alpha.getNumericValue()):1));
int b = (int)(blue.getIntegerValue() * (premultiplied?(1-alpha.getNumericValue()):1));
int r = (int)(rgbChannel(red) * (premultiplied?(1-alpha.getNumericValue()):1));
int g = (int)(rgbChannel(green) * (premultiplied?(1-alpha.getNumericValue()):1));
int b = (int)(rgbChannel(blue) * (premultiplied?(1-alpha.getNumericValue()):1));


return String.format( "%02X%02X%02X",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.designer.css;

import com.codename1.ui.css.CSSThemeCompiler;
import com.codename1.ui.util.MutableResource;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Hashtable;

/**
* The runtime compiler in core ({@link CSSThemeCompiler}, used for live reload and by
* applications that build a theme on the device) against this one, on the declarations both
* support: the same sheet has to store the same constants, padding, margin and unit metadata.
*/
public class RuntimeCompilerParityTest {

private static final String CSS = "#Constants {\n"
+ " includeNativeBool: true;\n"
+ " customMessage: \"Hello\";\n"
+ " quotedFlagBool: 'false';\n"
+ " accentColor: #ff2d95;\n"
+ " shortColor: #abc;\n"
+ " switchTrackScaleY: 1.5;\n"
+ " pctColor: rgb(100%, 0%, 50%);\n"
+ " clampColor: rgb(200%, 300, 0);\n"
+ "}\n"
+ "Label { padding: 1px 2px 3px 4px; margin: 1mm 2mm; }\n"
+ "One { padding: 5px; margin: 0.5mm; }\n"
+ "Three { padding: 1mm 2px 3mm; }\n"
+ "Mixed { padding: 1mm 2px 3pt 4mm; margin: 1px 2mm 3px 4mm; }\n"
+ "Mixed.pressed { padding: 2mm 1mm; }\n"
+ "Mixed.selected { margin: 3pt; }\n";

@BeforeAll
static void installHeadlessImplementation() throws Exception {
HeadlessTestSupport.installHeadlessImplementation();
}

@Test
void constantsMatch() throws Exception {
Hashtable standard = compileStandard(CSS);
Hashtable runtime = compileRuntime(CSS);
for (String key : new String[]{"@includeNativeBool", "@customMessage", "@quotedFlagBool"}) {
assertEquals(standard.get(key), runtime.get(key), key);
}
// Hex case differs between the compilers for every color, not just constants (the
// build-time one writes upper case); both are read with Integer.parseInt(value, 16).
for (String key : new String[]{"@accentColor", "@shortColor", "@pctColor", "@clampColor"}) {
assertEquals(Integer.valueOf(Integer.parseInt((String) standard.get(key), 16)),
Integer.valueOf(Integer.parseInt((String) runtime.get(key), 16)), key);
}
assertEquals("true", runtime.get("@includeNativeBool"), "@includeNativeBool");
assertEquals("Hello", runtime.get("@customMessage"), "@customMessage");
}

@Test
void spacingMatches() throws Exception {
Hashtable standard = compileStandard(CSS);
Hashtable runtime = compileRuntime(CSS);
String[] keys = {
"Label.padding", "Label.margin", "Label.padUnit", "Label.marUnit",
"One.padding", "One.margin", "One.padUnit", "One.marUnit",
"Three.padding", "Three.padUnit",
"Mixed.padding", "Mixed.margin", "Mixed.padUnit", "Mixed.marUnit",
"Mixed.press#padding", "Mixed.press#padUnit",
"Mixed.sel#margin", "Mixed.sel#marUnit"};
for (String key : keys) {
Object expected = standard.get(key);
Object actual = runtime.get(key);
if (expected == null || actual == null) {
throw new AssertionError(key + " standard=" + expected + " runtime=" + actual);
}
if (expected instanceof byte[]) {
if (!Arrays.equals((byte[]) expected, (byte[]) actual)) {
throw new AssertionError(key + " units standard=" + Arrays.toString((byte[]) expected)
+ " runtime=" + Arrays.toString((byte[]) actual));
}
} else if (!Arrays.equals(floats((String) expected), floats((String) actual))) {
throw new AssertionError(key + " standard=" + expected + " runtime=" + actual);
}
}
}

private static float[] floats(String box) {
String[] parts = box.split(",");
float[] out = new float[parts.length];
for (int i = 0; i < parts.length; i++) {
out[i] = Float.parseFloat(parts[i].trim());
}
return out;
}

private static Hashtable compileRuntime(String css) {
MutableResource resource = new MutableResource();
new CSSThemeCompiler().compile(css, resource, "Theme");
return resource.getTheme("Theme");
}

private static Hashtable compileStandard(String css) throws Exception {
Path cssFile = Files.createTempFile("cn1-parity", ".css");
Path resFile = Files.createTempFile("cn1-parity", ".res");
try {
Files.write(cssFile, css.getBytes(StandardCharsets.UTF_8));
CSSTheme theme = CSSTheme.load(cssFile.toUri().toURL());
theme.resourceFile = resFile.toFile();
theme.res = new com.codename1.ui.util.EditableResourcesForCSS(resFile.toFile());
theme.res.setTheme("Theme", new Hashtable());
theme.updateResources();
return theme.res.getTheme("Theme");
} finally {
deleteIfExists(cssFile);
deleteIfExists(resFile);
}
}

private static void deleteIfExists(Path path) {
try {
Files.deleteIfExists(path);
} catch (IOException ignored) {
}
}

private static void assertEquals(Object expected, Object actual, String message) {
if (expected == null ? actual != null : !expected.equals(actual)) {
throw new AssertionError(message + " expected=" + expected + " actual=" + actual);
}
}
}
2 changes: 1 addition & 1 deletion scripts/flutter-bench/baseline/pr/5976.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"tolerance": {
"value": 0.005
},
"value": 13238731
"value": 13304997
},
"reason": "JavaScript sizes on run 38943915580 (13,338,532 / 108,835,838 bytes) are above a baseline taken on the Flutter branch before later master merges; desktop-compat-layer's run 38946610873, which does not contain this PR, is further above it (13,356,239 / 108,853,545). The Flutter benchmark has no master run to separate this PR's share, which is ParparVM's JS output for float rounding (Math.fround) and the single-rounding long-to-float.",
"runs": 1,
Expand Down
33 changes: 33 additions & 0 deletions scripts/flutter-bench/baseline/pr/6032.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"pr": 6032,
"rebaseline": {
"linux": {
"gallery": {
"code_bytes": {
"from": {
"value": 14523602
},
"reason": "CSSThemeCompiler (runtime CSS theme compiler in core) gained build-compiler parity: state inheritance, per-side insets, media prefixes, var() bindings, the CSS named-color table",
"runs": 1,
"value": 14592242
},
"install_bytes": {
"from": {
"value": 116342496
},
"reason": "CSSThemeCompiler (runtime CSS theme compiler in core) gained build-compiler parity: state inheritance, per-side insets, media prefixes, var() bindings, the CSS named-color table",
"runs": 1,
"value": 116468448
},
"wire_bytes": {
"from": {
"value": 84685068
},
"reason": "CSSThemeCompiler (runtime CSS theme compiler in core) gained build-compiler parity: state inheritance, per-side insets, media prefixes, var() bindings, the CSS named-color table",
"runs": 1,
"value": 84687971
}
}
}
}
}
Loading
Loading