Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d51a78a
Extract CSS compiler into thin maven/css-compiler module
shai-almog Apr 22, 2026
5953a37
Add codenameone-css-compiler to root dependencyManagement
shai-almog Apr 22, 2026
a1c403d
Move css-compiler peer helpers + UIBuilderOverride hook
shai-almog Apr 22, 2026
cca4255
Fix remaining designer refs in EditableResources/ResourcesMutator
shai-almog Apr 22, 2026
a931c9d
Stop tracking .claude/ runtime files
shai-almog Apr 22, 2026
7251f98
Keep loadedResources typed as EditableResources; cast on getResourceE…
shai-almog Apr 22, 2026
eb01b16
Fix designer fat-jar unwrap in CI and build-native-themes stdout leak
shai-almog Apr 22, 2026
645a13c
Un-swallow CSSTheme.load NPEs, add null guard in NoCefCSSCLI
shai-almog Apr 22, 2026
a0a896a
Util.copy: fall back to direct stream close when no CN1 impl is set
shai-almog Apr 22, 2026
75b3857
Fix state-selector syntax in smoke native-themes CSS
shai-almog Apr 22, 2026
016884d
Drop font-family from smoke CSS until headless Font creation is sorted
shai-almog Apr 22, 2026
b080de4
Flesh out real iOS Modern + Android Material native themes
shai-almog Apr 22, 2026
68ede01
Use cn1-background-type syntax for native rounded/pill borders
shai-almog Apr 22, 2026
75d930c
Inline hex colors in themes; drop :root (mangles under dark-mode rewr…
shai-almog Apr 22, 2026
6274ba0
Headless guards: EditorTTFFont + Display.convertToPixels
shai-almog Apr 22, 2026
71429b4
Font.getFace/Size/Style: headless fallback to stored descriptor
shai-almog Apr 22, 2026
7e4d34f
Replace SVGDocument interface with internal reflective bridge
shai-almog Apr 22, 2026
636a16b
Phase 4: wire iOS + Android ports to new modern native themes
shai-almog Apr 22, 2026
59fedb5
Use /// markdown doc for headless Font fields (core validator)
shai-almog Apr 22, 2026
85472c8
Util.copy headless close: route through closeQuietly with stderr log
shai-almog Apr 22, 2026
48d5ab2
Phase 5: simulator bundles all themes + Native Theme override menu
shai-almog Apr 22, 2026
be36f3b
Util.closeQuietly: overload for InputStream and OutputStream
shai-almog Apr 22, 2026
6b5c476
Phase 6: build-hint schema defaults + docs
shai-almog Apr 22, 2026
47f76b0
Phase 7: theme-fidelity screenshot tests (first batch)
shai-almog Apr 22, 2026
bff6b45
Fix DualAppearanceBaseTest: drop package-private UIManager.getThemeProps
shai-almog Apr 22, 2026
e2f4545
Wire JavaScript port to CSS-driven native themes
shai-almog Apr 22, 2026
444f426
Util.closeQuietly: add braces to satisfy PMD ControlStatementBraces
shai-almog Apr 22, 2026
3bfcc05
Dark-mode screenshot fix + strengthen iOS disabled + expand test suite
shai-almog Apr 23, 2026
768725a
Fix SpanLabel / Picker imports in new theme-screenshot tests
shai-almog Apr 23, 2026
e6416e9
UIManager.refreshTheme() + drop test-side reflection
shai-almog Apr 23, 2026
e7e77b3
Skip theme tests in HTML5 port (tight browser-lifetime budget)
shai-almog Apr 23, 2026
84d3245
JS port: keep legacy theme default, upgrade only on explicit hint
shai-almog Apr 23, 2026
ffce1c6
JS port: add theme screenshot tests to port.js forced-timeout list
shai-almog Apr 23, 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
62 changes: 45 additions & 17 deletions .github/workflows/designer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ on:
- master
paths:
- 'CodenameOneDesigner/**'
- 'maven/css-compiler/**'
- 'maven/designer/**'
- '.github/workflows/designer.yml'
pull_request:
branches:
- master
paths:
- 'CodenameOneDesigner/**'
- 'maven/css-compiler/**'
- 'maven/designer/**'
- '.github/workflows/designer.yml'

Expand All @@ -38,30 +40,47 @@ jobs:
wget https://github.com/codenameone/cn1-binaries/archive/refs/heads/master.zip
unzip master.zip -d ..
mv ../cn1-binaries-master ../cn1-binaries
mkdir -p maven/target
rm -rf maven/target/cn1-binaries
cp -R ../cn1-binaries maven/target/cn1-binaries

- name: Build core dependencies
- name: Build Designer (pulls in codenameone-css-compiler as dep)
env:
CN1_BINARIES: ${{ github.workspace }}/maven/target/cn1-binaries
run: |
xvfb-run -a ant -noinput -buildfile Ports/CLDC11/build.xml jar
xvfb-run -a ant -noinput -buildfile Ports/JavaSE/build.xml jar
xvfb-run -a ant -noinput -buildfile Ports/JavaSEWithSVGSupport/build.xml jar
xvfb-run -a ant -noinput -buildfile CodenameOne/build.xml jar

- name: Run designer CSS localization tests
run: xvfb-run -a ant -noinput -buildfile CodenameOneDesigner/build.xml test-css-localization
cd maven
mvn -B -pl designer -am -DskipTests -Plocal-dev-javase \
-Dmaven.javadoc.skip=true -Dcn1.binaries="${CN1_BINARIES}" install

- name: Run designer XML parser unit tests (Maven)
env:
CN1_BINARIES: ${{ github.workspace }}/maven/target/cn1-binaries
run: |
mkdir -p maven/target
rm -rf maven/target/cn1-binaries
cp -R ../cn1-binaries maven/target/cn1-binaries
cd maven
mvn -B -pl designer -am -DunitTests=true -Dcodename1.platform=javase -Plocal-dev-javase -Dmaven.javadoc.skip=true -Dmaven.antrun.skip=true -Dtest=SimpleXmlParserTest -DfailIfNoTests=false test

- name: Build designer release jar
run: xvfb-run -a ant -noinput -buildfile CodenameOneDesigner/build.xml release
mvn -B -pl designer -am -DunitTests=true -Dcodename1.platform=javase \
-Plocal-dev-javase -Dmaven.javadoc.skip=true -Dmaven.antrun.skip=true \
-Dcn1.binaries="${CN1_BINARIES}" \
-Dtest=SimpleXmlParserTest -DfailIfNoTests=false test

- name: Verify designer CLI CSS compilation
run: |
# The Maven-built jar-with-dependencies is a ZIP wrapper around the
# actual runnable designer_1.jar (see the antrun
# add-designer-jar-with-dependencies execution in maven/designer/pom.xml).
# Unpack and run the inner jar directly.
wrapped=$(ls maven/designer/target/codenameone-designer-*-jar-with-dependencies.jar | head -n1)
if [ -z "${wrapped}" ] || [ ! -f "${wrapped}" ]; then
echo "designer jar-with-dependencies not found" >&2
exit 1
fi
extract_dir="$(mktemp -d)"
unzip -q "${wrapped}" -d "${extract_dir}"
designer_jar="${extract_dir}/designer_1.jar"
if [ ! -f "${designer_jar}" ]; then
echo "designer_1.jar not found inside ${wrapped}" >&2
ls -la "${extract_dir}"
exit 1
fi
tmp_dir="CodenameOneDesigner/tmp-cli-test"
css_file="$tmp_dir/test.css"
l10n_dir="$tmp_dir/localization"
Expand All @@ -75,12 +94,21 @@ jobs:
cat <<'EOF' > "$l10n_dir/Strings.properties"
greeting=Hello from CLI
EOF
xvfb-run -a java -Dcli=true -jar CodenameOneDesigner/dist/designer.jar \
xvfb-run -a java -Dcli=true -jar "${designer_jar}" \
-css -stateless -input "$css_file" -output "$output_file" -localization "$l10n_dir"
test -s "$output_file"

- name: Verify native-themes CEF-free build
run: |
cd maven
mvn -B -pl css-compiler -am install -DskipTests -Dmaven.javadoc.skip=true -Plocal-dev-javase
cd ..
./scripts/build-native-themes.sh
test -f Themes/iOSModernTheme.res || { echo "missing Themes/iOSModernTheme.res"; exit 1; }
test -f Themes/AndroidMaterialTheme.res || { echo "missing Themes/AndroidMaterialTheme.res"; exit 1; }

- name: Upload designer jar artifact
uses: actions/upload-artifact@v4
with:
name: designer-jar
path: CodenameOneDesigner/dist/designer.jar
path: maven/designer/target/codenameone-designer-*-jar-with-dependencies.jar
8 changes: 8 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ jobs:
cd maven
mvn clean verify -DunitTests=true -pl core-unittests -am -Dmaven.javadoc.skip=true -Plocal-dev-javase $MVN_ARGS
cd ..
- name: Build CSS compiler and smoke native-themes
run: |
cd maven
mvn -B -pl css-compiler -am install -DskipTests -Dmaven.javadoc.skip=true -Plocal-dev-javase
cd ..
./scripts/build-native-themes.sh
test -f Themes/iOSModernTheme.res || { echo "missing Themes/iOSModernTheme.res"; exit 1; }
test -f Themes/AndroidMaterialTheme.res || { echo "missing Themes/AndroidMaterialTheme.res"; exit 1; }
- name: Prepare Codename One binaries for Maven plugin tests
run: |
set -euo pipefail
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ node_modules
**/genfiles.properties
**/private/private.properties
.idea/
.claude/
target
pom.xml.versionsBackup
pom.xml.releaseBackup
Expand Down
35 changes: 33 additions & 2 deletions CodenameOne/src/com/codename1/io/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,39 @@ public static void copy(InputStream i, OutputStream o, int bufferSize) throws IO
try {
copyNoClose(i, o, bufferSize);
} finally {
Util.getImplementation().cleanup(o);
Util.getImplementation().cleanup(i);
CodenameOneImplementation impl = Util.getImplementation();
if (impl != null) {
impl.cleanup(o);
impl.cleanup(i);
} else {
// Headless callers (e.g. the css-compiler native-themes build)
// use this method before Display is initialized. Fall back to
// closing the streams directly so we do not NPE.
closeQuietly(o);
closeQuietly(i);
}
}
}

private static void closeQuietly(InputStream c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException e) {
System.err.println("Util.copy: ignoring " + e);
}
}

private static void closeQuietly(OutputStream c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException e) {
System.err.println("Util.copy: ignoring " + e);
}
}

Expand Down
8 changes: 8 additions & 0 deletions CodenameOne/src/com/codename1/ui/Display.java
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,14 @@ public int convertToPixels(float value, byte unitType, boolean horizontal) {
///
/// value in pixels
public int convertToPixels(float dipCount) {
if (impl == null) {
// Headless callers (e.g. the css-compiler native-themes build)
// compile theme constants before Display is initialized; return
// a 1:1 fallback so border/padding serialization succeeds. The
// actual pixel conversion happens at app runtime when a full
// implementation is available.
return Math.round(dipCount);
}
return Math.round(impl.convertToPixels((int) (dipCount * 1000), true) / 1000.0f);
}

Expand Down
27 changes: 26 additions & 1 deletion CodenameOne/src/com/codename1/ui/Font.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,27 @@ public class Font extends CN {
}

Font(int face, int style, int size) {
this.headlessFace = face;
this.headlessStyle = style;
this.headlessSize = size;
Display d = Display.getInstance();
CodenameOneImplementation i = d.getImplementation();
font = i.createFont(face, style, size);
if (i != null) {
font = i.createFont(face, style, size);
}
// Headless callers (e.g. the css-compiler native-themes build) never
// render text; the serialized theme only stores face/style/size and
// the native font object is recreated when the resource is loaded
// inside a running CN1 app.
}

/// Face/style/size copies used by getFace/getStyle/getSize when Display.impl
/// is null (headless native-themes build). Only populated by the system-font
/// constructor; other code paths (TTF, bitmap) keep them at zero.
private int headlessFace;
private int headlessStyle;
private int headlessSize;

/// Returns a previously loaded bitmap font from cache
///
/// #### Parameters
Expand Down Expand Up @@ -733,6 +749,9 @@ void drawChars(Graphics g, char[] data, int offset, int length, int x, int y) {
///
/// Optional operation returning the font face for system fonts
public int getFace() {
if (Display.impl == null) {
return headlessFace;
}
return Display.impl.getFace(font);
}

Expand All @@ -742,6 +761,9 @@ public int getFace() {
///
/// Optional operation returning the font size for system fonts
public int getSize() {
if (Display.impl == null) {
return headlessSize;
}
return Display.impl.getSize(font);
}

Expand All @@ -751,6 +773,9 @@ public int getSize() {
///
/// Optional operation returning the font style for system fonts
public int getStyle() {
if (Display.impl == null) {
return headlessStyle;
}
return Display.impl.getStyle(font);
}

Expand Down
19 changes: 19 additions & 0 deletions CodenameOne/src/com/codename1/ui/plaf/UIManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,25 @@ public void addThemeProps(Hashtable themeProps) {
}
}

/// Invalidates the cached Style instances and re-runs the theme build pass
/// against the currently installed theme properties. Callers use this after
/// state changes that affect style resolution (notably `Display.setDarkMode`,
/// which makes `$Dark<UIID>` entries eligible) without reloading the theme
/// from a resource file. Components styled after this call resolve against
/// the refreshed theme; already-resolved Style references on existing
/// components keep their old values until those components re-fetch their
/// styles.
public void refreshTheme() {
if (!accessible || themeProps == null) {
return;
}
Hashtable props = new Hashtable();
for (String key : themeProps.keySet()) {
props.put(key, themeProps.get(key));
}
setThemePropsImpl(props);
}

/// Returns a theme constant defined in the resource editor
///
/// #### Parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package com.codename1.designer;

import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.EditableResourcesEditor;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
Expand Down Expand Up @@ -198,7 +199,7 @@ public String addResource(EditableResources res, ResourceEditorView view) {
InputStream is = getClass().getResourceAsStream("/templates/" + template.getSelectedItem().toString() + ".res");
if(is != null) {
try {
EditableResources r = new EditableResources();
EditableResources r = new EditableResourcesEditor();
r.openFile(is);
is.close();
theme = r.getTheme(r.getThemeResourceNames()[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package com.codename1.designer;

import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.EditableResourcesEditor;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
Expand Down Expand Up @@ -199,7 +200,7 @@ public String addResource(EditableResources res, ResourceEditorView view) {
InputStream is = getClass().getResourceAsStream("/templates/" + template.getSelectedItem().toString() + ".res");
if(is != null) {
try {
EditableResources r = new EditableResources();
EditableResources r = new EditableResourcesEditor();
r.openFile(is);
is.close();
ui = r.getResourceObject("Main");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
//import com.codename1.impl.javase.JavaFXLoader;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.resource.util.QuitAction;
import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.EditableResources;
import com.codename1.ui.util.EditableResourcesEditor;
import com.codename1.ui.util.Resources;
import com.codename1.ui.util.UIBuilderOverride;
import java.awt.BorderLayout;
Expand Down Expand Up @@ -298,7 +299,7 @@ public static void _main(String[] args) throws Exception {

boolean isXMLEnabled = Preferences.userNodeForPackage(ResourceEditorView.class).getBoolean("XMLFileMode", true);
EditableResources.setXMLEnabled(isXMLEnabled);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();
File resourceFile = new File(args[1]);
res.openFileWithXMLSupport(resourceFile);

Expand Down Expand Up @@ -424,7 +425,7 @@ public void actionPerformed(ActionEvent e) {

boolean isXMLEnabled = Preferences.userNodeForPackage(ResourceEditorView.class).getBoolean("XMLFileMode", true);
EditableResources.setXMLEnabled(isXMLEnabled);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();

res.openFileWithXMLSupport(resourceFile);
res.setImage(imageName, img);
Expand Down Expand Up @@ -499,7 +500,7 @@ public void actionPerformed(ActionEvent e) {

boolean isXMLEnabled = Preferences.userNodeForPackage(ResourceEditorView.class).getBoolean("XMLFileMode", true);
EditableResources.setXMLEnabled(isXMLEnabled);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();

res.openFileWithXMLSupport(resourceFile);
AddAndScaleMultiImage.generateImpl(new File[] {imageFile},
Expand All @@ -524,7 +525,7 @@ public void actionPerformed(ActionEvent e) {
com.codename1.ui.Display.init(cnt);
File projectDir = new File(args[1]);
EditableResources.setXMLEnabled(true);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();
res.openFileWithXMLSupport(new File(args[2]));
migrateGuiBuilder(projectDir, res, args[3]);
System.exit(0);
Expand All @@ -535,7 +536,7 @@ public void actionPerformed(ActionEvent e) {
com.codename1.ui.Display.init(cnt);
File output = new File(args[1]);
EditableResources.setXMLEnabled(true);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();
res.openFileWithXMLSupport(output);
FileOutputStream fos = new FileOutputStream(output);
res.save(fos);
Expand All @@ -550,7 +551,7 @@ public void actionPerformed(ActionEvent e) {
com.codename1.ui.Display.init(cnt);
File output = new File(args[1]);
EditableResources.setXMLEnabled(true);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();
res.openFileWithXMLSupport(output);
FileOutputStream fos = new FileOutputStream(output);
res.save(fos);
Expand Down Expand Up @@ -579,7 +580,7 @@ public void actionPerformed(ActionEvent e) {

private static void generateResourceFile(File f, String themeName, String ui) throws Exception {
System.out.println("Generating resource file " + f + " theme " + themeName + " template " + ui);
EditableResources res = new EditableResources();
EditableResources res = new EditableResourcesEditor();

//"native", "leather", "tzone", "tipster", "blank"
String template = "Native_Theme";
Expand Down Expand Up @@ -1232,7 +1233,7 @@ private static Hashtable importRes(EditableResources res, String file) {
Hashtable theme = new Hashtable();
if(is != null) {
try {
EditableResources r = new EditableResources();
EditableResources r = new EditableResourcesEditor();
r.openFile(is);
is.close();
if(r.getThemeResourceNames().length > 0) {
Expand Down
Loading
Loading