Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
9537541
Extract CSS compiler into thin maven/css-compiler module
shai-almog Apr 22, 2026
b0d5737
Add codenameone-css-compiler to root dependencyManagement
shai-almog Apr 22, 2026
9eb4c91
Move css-compiler peer helpers + UIBuilderOverride hook
shai-almog Apr 22, 2026
3395c21
Fix remaining designer refs in EditableResources/ResourcesMutator
shai-almog Apr 22, 2026
e54c56a
Stop tracking .claude/ runtime files
shai-almog Apr 22, 2026
89f9e61
Keep loadedResources typed as EditableResources; cast on getResourceE…
shai-almog Apr 22, 2026
7d27779
Fix designer fat-jar unwrap in CI and build-native-themes stdout leak
shai-almog Apr 22, 2026
6d0be78
Un-swallow CSSTheme.load NPEs, add null guard in NoCefCSSCLI
shai-almog Apr 22, 2026
09bb0fb
Util.copy: fall back to direct stream close when no CN1 impl is set
shai-almog Apr 22, 2026
68d8f40
Fix state-selector syntax in smoke native-themes CSS
shai-almog Apr 22, 2026
2edf99a
Drop font-family from smoke CSS until headless Font creation is sorted
shai-almog Apr 22, 2026
19c83af
Flesh out real iOS Modern + Android Material native themes
shai-almog Apr 22, 2026
ffde728
Use cn1-background-type syntax for native rounded/pill borders
shai-almog Apr 22, 2026
a77ae47
Inline hex colors in themes; drop :root (mangles under dark-mode rewr…
shai-almog Apr 22, 2026
5252c23
Headless guards: EditorTTFFont + Display.convertToPixels
shai-almog Apr 22, 2026
8dd5075
Font.getFace/Size/Style: headless fallback to stored descriptor
shai-almog Apr 22, 2026
bfc0451
Replace SVGDocument interface with internal reflective bridge
shai-almog Apr 22, 2026
2f62c92
Phase 4: wire iOS + Android ports to new modern native themes
shai-almog Apr 22, 2026
dfb6140
Use /// markdown doc for headless Font fields (core validator)
shai-almog Apr 22, 2026
b1ba3de
Util.copy headless close: route through closeQuietly with stderr log
shai-almog Apr 22, 2026
e80fb41
Phase 5: simulator bundles all themes + Native Theme override menu
shai-almog Apr 22, 2026
29b1d0b
Util.closeQuietly: overload for InputStream and OutputStream
shai-almog Apr 22, 2026
d7b5acc
Phase 6: build-hint schema defaults + docs
shai-almog Apr 22, 2026
f28bf63
Phase 7: theme-fidelity screenshot tests (first batch)
shai-almog Apr 22, 2026
122b834
Fix DualAppearanceBaseTest: drop package-private UIManager.getThemeProps
shai-almog Apr 22, 2026
498b370
Wire JavaScript port to CSS-driven native themes
shai-almog Apr 22, 2026
8a33671
Util.closeQuietly: add braces to satisfy PMD ControlStatementBraces
shai-almog Apr 22, 2026
3370849
Dark-mode screenshot fix + strengthen iOS disabled + expand test suite
shai-almog Apr 23, 2026
7dec1fa
Fix SpanLabel / Picker imports in new theme-screenshot tests
shai-almog Apr 23, 2026
75b5d04
UIManager.refreshTheme() + drop test-side reflection
shai-almog Apr 23, 2026
b66ea05
Skip theme tests in HTML5 port (tight browser-lifetime budget)
shai-almog Apr 23, 2026
e48bc23
JS port: keep legacy theme default, upgrade only on explicit hint
shai-almog Apr 23, 2026
131825d
JS port: add theme screenshot tests to port.js forced-timeout list
shai-almog Apr 23, 2026
cedd55c
Ship native themes in iOS/Android builds; fix Switch/Tabs; add grid +…
shai-almog Apr 23, 2026
6f1080a
Drop :root CSS-var block; inline colors
shai-almog Apr 23, 2026
cddfcd7
Strip @media from theme file header comments
shai-almog Apr 23, 2026
a06359e
Compiler: skip @media inside block comments (dark-mode rewriter)
shai-almog Apr 23, 2026
0f3f493
Compiler: actually emit $Dark<UIID> entries from @media (dark)
shai-almog Apr 23, 2026
7e9ace8
Fix duplicate PNG captures, switch to designer-style overlay, border-…
shai-almog Apr 23, 2026
ed7b781
Fill in dark-mode gaps: TitleArea, MainTitle, SpanLabel, TextArea, etc
shai-almog Apr 23, 2026
0db7dc5
Keep legacy themes default; new tests opt-in modern; per-component grid
shai-almog Apr 23, 2026
aa4fdfa
DualAppearance: log whether modern theme actually loaded
shai-almog Apr 23, 2026
4cf58ab
Load modern theme via Display.getResourceAsStream (Android assets path)
shai-almog Apr 23, 2026
01c01c5
Android default theme: holo light, not pre-Holo androidTheme.res
shai-almog Apr 23, 2026
c74a157
DualAppearance.finish: reload /theme.res, not just native theme
shai-almog Apr 23, 2026
529bede
DualAppearance: diagnose iOS theme-load + classpath fallback
shai-almog Apr 23, 2026
dbc5b05
DualAppearance: trace each step of runAppearance
shai-almog Apr 23, 2026
cd89c6a
DualAppearance: split Form ctor to find the hang
shai-almog Apr 23, 2026
dc0a8a3
DualAppearance: probe each UIID before the Form ctor
shai-almog Apr 23, 2026
ff79737
iOS modern theme: inline TitleArea props (no cn1-derive)
shai-almog Apr 23, 2026
6861dc3
DualAppearance: drop probe/step diagnostics now that iOS works
shai-almog Apr 24, 2026
c596eeb
Install theme-fidelity goldens for iOS + Android
shai-almog Apr 24, 2026
76309d4
Modern themes: bottom tabs, distinct Dialog bg, explicit Title bg, si…
shai-almog Apr 24, 2026
f415232
Remove stale theme goldens pre-recapture
shai-almog Apr 24, 2026
98c7649
native-themes/README: document cn1-derive + backdrop-filter rules
shai-almog Apr 24, 2026
7c17458
Tabs: honor tabPlacementInt theme constant via setTabPlacement()
shai-almog Apr 24, 2026
882527a
Tabs.initLaf: null-guard tabsContainer for super()-chain call
shai-almog Apr 24, 2026
69f040f
Tabs ctor: read tabPlacementInt directly for no-arg construction
shai-almog Apr 24, 2026
2b9121e
Tabs ctor: log tabPlacementInt read for CI diagnosis
shai-almog Apr 24, 2026
a733a76
UIManager.refreshTheme: preserve theme constants across the round-trip
shai-almog Apr 24, 2026
ea3fa0d
Theme pass 2: Android compactness, iOS pill tabs, button text-align, …
shai-almog Apr 24, 2026
3da3985
Developer guide: new chapter for native modern themes
shai-almog Apr 24, 2026
2726546
Theme pass 3: textured backdrop, iOS MultiButton, per-platform check/…
shai-almog Apr 24, 2026
fbdf56e
DualAppearance: paint textured backdrop via paintBackground override
shai-almog Apr 24, 2026
82c2483
DualAppearance: clear ContentPane / TitleArea bg when textured
shai-almog Apr 24, 2026
c2a10a2
Theme pass 4: remove core hacks, rewrite docs, polish translucency + …
shai-almog Apr 25, 2026
43d0e25
initializr: opt new projects into the modern native themes
shai-almog Apr 25, 2026
dfa08d8
cn1playground: preview the modern native themes
shai-almog Apr 25, 2026
c450751
Font: drop unused CodenameOneImplementation import
shai-almog Apr 25, 2026
eaf21dd
Theme pass 5: cn1-derive cycle fix + Dialog/Tab polish + generative t…
shai-almog Apr 25, 2026
11662c5
SpanLabel transparent so it doesn't paint over translucent surfaces
shai-almog Apr 25, 2026
de5110a
Container transparent: fixes SpanLabel/wrapper opaque-block bug
shai-almog Apr 25, 2026
f0dab80
Label transparent: SpanLabel internal TextArea uses UIID=Label
shai-almog Apr 25, 2026
c58c0fe
Fix iOS Tabs pill gap, SpotBugs warnings, cn1playground preview
shai-almog Apr 25, 2026
d7c563f
Wrap Tabs pill in safe-area host; force modern theme in initializr
shai-almog Apr 25, 2026
446e8c7
Use short Map.Entry in UIManager.refreshTheme
shai-almog Apr 26, 2026
eba268a
Restore pill-border on iOS dark Tab.selected/pressed
shai-almog Apr 26, 2026
cd081fd
Scope initializr modern theme overlay to preview only
shai-almog Apr 26, 2026
82c7c71
Scope cn1playground modern theme overlay to preview only
shai-almog Apr 26, 2026
e5f0e4a
Bundle modern themes in JS apps; fix dark Tab icon square
shai-almog Apr 26, 2026
38caecf
build-native-themes: send mvn output to stderr so jar path stays clean
shai-almog Apr 26, 2026
ececffe
Stop modern overlay from breaking host UIs; opaque dark Form; pill Bu…
shai-almog Apr 26, 2026
252528c
Fix CSS compiler dropping rules with comments inside @media (dark)
shai-almog Apr 26, 2026
f752288
UIManager: break TitleArea<->Toolbar derive cycle after layered build
shai-almog Apr 26, 2026
e1a1ea3
iOS Modern: inline TitleArea instead of cn1-derive Toolbar
shai-almog Apr 26, 2026
033b761
Preview-side patch for TitleArea/Toolbar derive cycle
shai-almog Apr 26, 2026
048eb5f
Fix host CSS rgba shorthand, align preview bg, unstyle MultiButton
shai-almog Apr 26, 2026
3b7e023
Revert non-zero Dialog margin (iOS + Android modern themes)
shai-almog Apr 26, 2026
86ed0f0
Preview: Form bg behind non-Form previews + dialog sizing constants
shai-almog Apr 26, 2026
18be8dc
Polish title in preview, showcase modern UIIDs in demos, refresh goldens
shai-almog Apr 26, 2026
60f5ebc
Fix demo bugs (setOn, setScrollable), Android Button jump, tab toggle
shai-almog Apr 26, 2026
2f29a8d
Date Picker demo: import com.codename1.ui.spinner.*
shai-almog Apr 26, 2026
548c9ee
iOS goldens: revert flaky landscape, add missing TabsTheme_dark
shai-almog Apr 27, 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
2 changes: 1 addition & 1 deletion CodenameOne/src/com/codename1/io/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public static void copy(InputStream i, OutputStream o, int bufferSize) throws IO
}
}

/// Closes the object (connection, stream etc.) without throwing any exception, even if the
/// Closes the object (connection, stream etc.) without throwing any exception, even if the
/// object is null
///
/// #### Parameters
Expand Down
5 changes: 1 addition & 4 deletions CodenameOne/src/com/codename1/ui/Font.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
*/
package com.codename1.ui;

import com.codename1.impl.CodenameOneImplementation;

import java.util.HashMap;
import java.util.Hashtable;
Expand Down Expand Up @@ -171,9 +170,7 @@ public class Font extends CN {
}

Font(int face, int style, int size) {
Display d = Display.getInstance();
CodenameOneImplementation i = d.getImplementation();
font = i.createFont(face, style, size);
font = Display.getInstance().getImplementation().createFont(face, style, size);
}

/// Returns a previously loaded bitmap font from cache
Expand Down
82 changes: 73 additions & 9 deletions CodenameOne/src/com/codename1/ui/Tabs.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@
public class Tabs extends Container {
private final Container contentPane = new Container(new TabsLayout());
private final Container tabsContainer;
/// Optional wrapper around `tabsContainer` whose only job is to absorb the
/// safe-area inset when the theme opts out of internal safe-area padding
/// via the `tabsSafeAreaBool` constant. With the wrapper present, the
/// pill (`tabsContainer`) draws tightly while the wrapper's padding keeps
/// the pill clear of the home indicator. `null` for legacy themes that
/// keep the safe-area inset on the pill itself.
private Container tabsContainerHost;
private final ButtonGroup radioGroup = new ButtonGroup();
private final ActionListener press;
private final ActionListener drag;
Expand Down Expand Up @@ -151,11 +158,37 @@ public Tabs(int tabP) {
contentPane.setUIID("TabbedPane");
super.addComponent(BorderLayout.CENTER, contentPane);
tabsContainer = new Container();
tabsContainer.setSafeArea(true);
// tabsSafeAreaBool=true (default): legacy / flush-bar themes keep the
// safe-area inset as PADDING on the pill itself - the bar's
// background reaches the screen edge with tabs sitting above the
// home indicator.
//
// tabsSafeAreaBool=false (modern floating pill): the safe-area inset
// moves to a wrapper container so the pill draws tightly and is
// pushed up away from the indicator without extending its own
// background into the indicator zone.
boolean tabsSafeAreaOnPill = getUIManager().isThemeConstant("tabsSafeAreaBool", true);
tabsContainer.setSafeArea(tabsSafeAreaOnPill);
tabsContainer.setUIID("TabsContainer");
tabsContainer.setScrollVisible(false);
tabsContainer.getStyle().setMargin(0, 0, 0, 0);
if (!tabsSafeAreaOnPill) {
tabsContainerHost = new Container(new BorderLayout());
tabsContainerHost.setUIID("Container");
tabsContainerHost.setSafeArea(true);
tabsContainerHost.add(BorderLayout.CENTER, tabsContainer);
}
if (tabP == -1) {
// Honor the tabPlacementInt theme constant when no explicit
// placement was requested. Reading the constant here (rather
// than only in initLaf) guarantees the value is seen even
// when initLaf runs polymorphically from Component()'s super
// ctor - at that point the Tabs subclass fields haven't been
// initialised yet and writes to them are brittle.
int themePlacement = getUIManager().getThemeConstant("tabPlacementInt", -1);
if (themePlacement != -1) {
tabPlacement = themePlacement;
}
setTabPlacement(tabPlacement);
} else {
setTabPlacement(tabP);
Expand Down Expand Up @@ -216,8 +249,20 @@ protected void initLaf(UIManager manager) {
}
}
changeTabContainerStyleOnFocus = manager.isThemeConstant("changeTabContainerStyleOnFocusBool", false);
// tabPlacementInt lets a theme dictate whether tabs live at TOP /
// BOTTOM / LEFT / RIGHT. initLaf is called both during the
// Component() super() chain (before the Tabs ctor body has
// allocated tabsContainer) and again later when styles refresh.
// First call: tabsContainer is null, so just stash the value in
// the field; the ctor's setTabPlacement call at the end will
// pick it up and move the (then-allocated) container.
// Second call and beyond: container exists, so reparent it.
if (tabPlace != -1) {
tabPlacement = tabPlace;
if (tabsContainer == null) {
tabPlacement = tabPlace;
} else if (tabPlace != tabPlacement) {
setTabPlacement(tabPlace);
}
}
}

Expand Down Expand Up @@ -588,11 +633,28 @@ protected Component createTab(String title, Font font, char icon, float size) {
if (tabUIID != null) {
b.setUIID(tabUIID);
}
applyTabIconUIID(b);
b.setFontIcon(font, icon, size);
createTabImpl(b);
return b;
}

/// Detaches the tab's icon style from the Button's selection-state styles.
/// FontImage.setIcon copies the Button's unselected/selected/pressed styles
/// to render four icon variants - which means the icon image carries the
/// Button's bgColor and bgTransparency. With a `cn1-pill-border` selected
/// background, that produces a visible square fill behind the glyph that
/// doesn't follow the pill's rounded shape. Reading `tabIconUIID` from the
/// theme lets a theme route the icon styling to a separate UIID
/// (typically `TabIcon`) where it can be declared transparent. Themes that
/// don't define the constant get the legacy behavior unchanged.
private void applyTabIconUIID(Component b) {
String iconUiid = getUIManager().getThemeConstant("tabIconUIID", null);
if (iconUiid != null && iconUiid.length() > 0 && b instanceof Label) {
((Label) b).setIconUIID(iconUiid);
}
}

/// Creates a tab component by default this is a RadioButton but subclasses can use this to return anything
///
/// #### Parameters
Expand All @@ -606,6 +668,7 @@ protected Component createTab(String title, Font font, char icon, float size) {
/// component instance
protected Component createTab(String title, Image icon) {
RadioButton b = new RadioButton(title != null ? title : "", icon);
applyTabIconUIID(b);
createTabImpl(b);
return b;
}
Expand Down Expand Up @@ -1135,22 +1198,23 @@ public void setTabPlacement(int tabPlacement) {
tabPlacement != BOTTOM && tabPlacement != RIGHT) {
throw new IllegalArgumentException("illegal tab placement: must be TOP, BOTTOM, LEFT, or RIGHT");
}
if (this.tabPlacement == tabPlacement && tabsContainer.getParent() == null && isInitialized()) {
Container slotComponent = tabsContainerHost != null ? tabsContainerHost : tabsContainer;
if (this.tabPlacement == tabPlacement && slotComponent.getParent() == null && isInitialized()) {
return;
}
this.tabPlacement = tabPlacement;
removeComponent(tabsContainer);
removeComponent(slotComponent);

setTabsLayout(tabPlacement);

if (tabPlacement == TOP) {
super.addComponent(BorderLayout.NORTH, tabsContainer);
super.addComponent(BorderLayout.NORTH, slotComponent);
} else if (tabPlacement == BOTTOM) {
super.addComponent(BorderLayout.SOUTH, tabsContainer);
super.addComponent(BorderLayout.SOUTH, slotComponent);
} else if (tabPlacement == LEFT) {
super.addComponent(BorderLayout.WEST, tabsContainer);
super.addComponent(BorderLayout.WEST, slotComponent);
} else { // RIGHT
super.addComponent(BorderLayout.EAST, tabsContainer);
super.addComponent(BorderLayout.EAST, slotComponent);
}

initTabsFocus();
Expand Down Expand Up @@ -1238,7 +1302,7 @@ protected void selectTab(Component tab) {

/// Hide the tabs bar
public void hideTabs() {
removeComponent(tabsContainer);
removeComponent(tabsContainerHost != null ? tabsContainerHost : tabsContainer);
revalidateLater();
}

Expand Down
39 changes: 27 additions & 12 deletions CodenameOne/src/com/codename1/ui/plaf/DefaultLookAndFeel.java
Original file line number Diff line number Diff line change
Expand Up @@ -2443,15 +2443,24 @@ private void updateCheckBoxConstants(UIManager m, boolean focus, String append)
Style unsel = uim.createStyle("CheckBox.", "", false);
Style sel = uim.createStyle("CheckBox.", "sel#", true);
Style dis = uim.createStyle("CheckBox.", "dis#", false);
FontImage checkedDis = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX, dis);
FontImage uncheckedDis = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX_OUTLINE_BLANK, sel);
// Optional theme constants to swap the default Material
// check-box icons. Useful for platform-native looks (e.g.
// iOS modern uses CHECK_CIRCLE / RADIO_BUTTON_UNCHECKED
// for a rounded-circle aesthetic). Defaults to the legacy
// square check-box glyphs so existing themes are unaffected.
char checkedIcon = (char) uim.getThemeConstant(
"checkBoxCheckedIconInt", FontImage.MATERIAL_CHECK_BOX);
char uncheckedIcon = (char) uim.getThemeConstant(
"checkBoxUncheckedIconInt", FontImage.MATERIAL_CHECK_BOX_OUTLINE_BLANK);
FontImage checkedDis = FontImage.createMaterial(checkedIcon, dis);
FontImage uncheckedDis = FontImage.createMaterial(uncheckedIcon, sel);
if (focus) {
FontImage checkedSelected = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX, sel);
FontImage uncheckedSelected = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX_OUTLINE_BLANK, sel);
FontImage checkedSelected = FontImage.createMaterial(checkedIcon, sel);
FontImage uncheckedSelected = FontImage.createMaterial(uncheckedIcon, sel);
setCheckBoxFocusImages(checkedSelected, uncheckedSelected, checkedDis, uncheckedDis);
} else {
FontImage checkedUnselected = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX, unsel);
FontImage uncheckedUnselected = FontImage.createMaterial(FontImage.MATERIAL_CHECK_BOX_OUTLINE_BLANK, unsel);
FontImage checkedUnselected = FontImage.createMaterial(checkedIcon, unsel);
FontImage uncheckedUnselected = FontImage.createMaterial(uncheckedIcon, unsel);
setCheckBoxImages(checkedUnselected, uncheckedUnselected, checkedDis, uncheckedDis);
}
}
Expand Down Expand Up @@ -2484,15 +2493,21 @@ private void updateRadioButtonConstants(UIManager m, boolean focus, String appen
Style unsel = uim.createStyle("RadioButton.", "", false);
Style sel = uim.createStyle("RadioButton.", "sel#", true);
Style dis = uim.createStyle("RadioButton.", "dis#", false);
FontImage checkedDis = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, dis);
FontImage uncheckedDis = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, sel);
// Same override pattern as the check-box icons above -
// theme constants can swap the default circle glyphs.
char checkedIcon = (char) uim.getThemeConstant(
"radioCheckedIconInt", FontImage.MATERIAL_RADIO_BUTTON_CHECKED);
char uncheckedIcon = (char) uim.getThemeConstant(
"radioUncheckedIconInt", FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED);
FontImage checkedDis = FontImage.createMaterial(checkedIcon, dis);
FontImage uncheckedDis = FontImage.createMaterial(uncheckedIcon, sel);
if (focus) {
FontImage checkedSelected = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, sel);
FontImage uncheckedSelected = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, sel);
FontImage checkedSelected = FontImage.createMaterial(checkedIcon, sel);
FontImage uncheckedSelected = FontImage.createMaterial(uncheckedIcon, sel);
setRadioButtonFocusImages(checkedSelected, uncheckedSelected, checkedDis, uncheckedDis);
} else {
FontImage checkedUnselected = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, unsel);
FontImage uncheckedUnselected = FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, unsel);
FontImage checkedUnselected = FontImage.createMaterial(checkedIcon, unsel);
FontImage uncheckedUnselected = FontImage.createMaterial(uncheckedIcon, unsel);
setRadioButtonImages(checkedUnselected, uncheckedUnselected, checkedDis, uncheckedDis);
}
}
Expand Down
Loading
Loading