diff --git a/.github/workflows/developer-guide-docs.yml b/.github/workflows/developer-guide-docs.yml index 2a75558023c..8bb420da735 100644 --- a/.github/workflows/developer-guide-docs.yml +++ b/.github/workflows/developer-guide-docs.yml @@ -22,6 +22,26 @@ on: - 'maven/build-hint-catalog/**' - 'maven/build-hint-tools/**' - 'scripts/gen-build-hint-table.sh' + # The themed figures are rendered against the compiled native themes, so + # what those themes look like is an input to this guide exactly the way the + # build hint sources above are. build-native-themes.sh compiles + # native-themes//theme.css with maven/css-compiler, so a change to + # any of the three changes the pictures without touching docs/ at all -- + # and the committed figures would then go stale silently until some later + # run failed against them. + - 'native-themes/**' + - 'maven/css-compiler/**' + - 'scripts/build-native-themes.sh' + # The figures are rendered by the framework itself, so a change to how a + # component paints, how a layout measures, or how the JavaSE port + # rasterizes changes every generated PNG without touching docs/ at all. + # The comparison is byte-exact, so without these the committed figures go + # stale silently and some later docs or release build is the one that + # fails. + - 'CodenameOne/**' + - 'Ports/JavaSE/**' + - 'maven/core/**' + - 'maven/javase/**' release: types: [published] workflow_dispatch: @@ -67,6 +87,18 @@ jobs: - 'maven/build-hint-catalog/**' - 'maven/build-hint-tools/**' - 'scripts/gen-build-hint-table.sh' + # Same two-copies-of-one-list problem as the build hint inputs: + # triggering the workflow is not enough, because the figure + # comparison step is gated on this filter too. + - 'native-themes/**' + - 'maven/css-compiler/**' + - 'scripts/build-native-themes.sh' + # Same reason, and the same two-lists rule: the figure comparison + # step is gated on this filter as well as on the trigger. + - 'CodenameOne/**' + - 'Ports/JavaSE/**' + - 'maven/core/**' + - 'maven/javase/**' workflow: - '.github/workflows/developer-guide-docs.yml' @@ -85,6 +117,17 @@ jobs: - name: Render the build hint table run: scripts/gen-build-hint-table.sh + # The javase jar carries iOSModernTheme.res and AndroidMaterialTheme.res, + # which the figure renderer installs. They are copied in from Themes/ by an + # antrun step with failonerror=false, so if they have not been compiled the + # copy fails open and the jar ships without them -- and the renderer then + # dies on a theme resource that is simply absent. Compiling them first is + # what run-javase-simulator-integration-tests.sh already does, for the same + # reason. + - name: Compile the native themes + if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true' + run: ./scripts/build-native-themes.sh + - name: Install local Codename One Maven artifacts if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true' run: | @@ -122,7 +165,7 @@ jobs: install \ -DskipTests - - name: Verify pre-Advanced Theming screenshots are current + - name: Verify generated developer guide figures are current if: github.event_name != 'pull_request' || steps.changes.outputs.docs == 'true' || steps.changes.outputs.demos == 'true' || steps.changes.outputs.workflow == 'true' run: | set -euo pipefail @@ -142,8 +185,7 @@ jobs: # difference if it carries a .tolerance sidecar saying why. python3 scripts/developer-guide/compare-screenshots.py \ --generated "$GENERATED_DIR" \ - --committed docs/developer-guide/img \ - --expected-count 24 + --committed docs/developer-guide/img/generated # When the byte compare fails, the message names the file but not what it # actually rendered, which leaves no way to tell a real regression from an diff --git a/CodenameOne/src/com/codename1/ui/package-info.java b/CodenameOne/src/com/codename1/ui/package-info.java index db05bd3316e..0cf4aee799d 100644 --- a/CodenameOne/src/com/codename1/ui/package-info.java +++ b/CodenameOne/src/com/codename1/ui/package-info.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2012, 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. + */ + /// Main widget package containing the component/container "composite" similar /// both in terminology and design to Swing/AWT. /// @@ -37,31 +60,31 @@ /// /// ### AutoCompleteTextField /// -/// ![](https://www.codenameone.com/img/developer-guide/components-autocomplete.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-autocomplete.png) /// `com.codename1.ui.AutoCompleteTextField` provides suggestions as you type into the text /// field /// /// /// ### BrowserComponent /// -/// ![](https://www.codenameone.com/img/developer-guide/components-browsercomponent.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-browsercomponent.png) /// `com.codename1.ui.BrowserComponent` allows us to embed an OS native browser into the app and /// connect to its JavaScript runtime! /// /// /// ### Button /// -/// ![](https://www.codenameone.com/img/developer-guide/components-button.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-button-ios.png) /// `com.codename1.ui.Button` allows us to bind events to a click /// /// ### Link Button /// -/// ![](https://www.codenameone.com/img/developer-guide/components-link-button.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-link-button.png) /// `com.codename1.ui.Button` can also be used as a hyperlink /// /// ### Calendar /// -/// ![](https://www.codenameone.com/img/developer-guide/components-calendar.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-calendar.png) /// `com.codename1.ui.Calendar` presents a visual date picker. Notice that we recommend using /// the /// `com.codename1.ui.spinner.Picker` class which is superior when running on the device for most use cases. @@ -69,7 +92,7 @@ /// /// ### CheckBox /// -/// ![](https://www.codenameone.com/img/developer-guide/components-radiobutton-checkbox.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-radiobutton-checkbox.png) /// `com.codename1.ui.CheckBox` provides a check flag to tick on/off. /// `com.codename1.ui.RadioButton` provides an exclusive check marking that only applies to one radio within /// the group. @@ -78,7 +101,7 @@ /// /// ### ComboBox /// -/// ![](https://www.codenameone.com/img/developer-guide/components-combobox.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-combobox.png) /// `com.codename1.ui.ComboBox` is a list with a single visible entry that can popup the full /// list. Notice that we recommend using the /// `com.codename1.ui.spinner.Picker` class which is superior when running on the device for most use cases @@ -86,14 +109,14 @@ /// /// ### Command /// -/// ![](https://www.codenameone.com/img/developer-guide/components-toolbar.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-toolbar.png) /// `com.codename1.ui.Command` & `com.codename1.ui.Toolbar` provide deep customization /// of the title area and allow us to place elements in the side menu (hamburger), overflow menu etc. /// /// /// ### ComponentGroup /// -/// ![](https://www.codenameone.com/img/developer-guide/components-componentgroup.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-componentgroup.png) /// `com.codename1.ui.ComponentGroup` allows us to group components together in a a group and /// manipulate /// their UIID's. @@ -101,24 +124,24 @@ /// /// ### Dialog /// -/// ![](https://www.codenameone.com/img/developer-guide/components-dialog-modal-south.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-dialog-modal-south.png) /// `com.codename1.ui.Dialog` allows us to notify/ask the user in a modal/modless way. /// /// ### InfiniteContainer /// -/// ![](https://www.codenameone.com/img/developer-guide/components-infinitescrolladapter.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-infinitescrolladapter.png) /// `com.codename1.ui.InfiniteContainer` & `com.codename1.components.InfiniteScrollAdapter` /// implement a `com.codename1.ui.Container` that can dynamically fetch more data /// /// /// ### Label /// -/// ![](https://www.codenameone.com/img/developer-guide/components-label-text-position.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-label-text-position.png) /// `com.codename1.ui.Label` displays text and/or icons to the user /// /// ### List /// -/// ![](https://www.codenameone.com/img/developer-guide/components-generic-list-cell-renderer.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-generic-list-cell-renderer.png) /// `com.codename1.ui.List` a list of items, this is a rather elaborate component to work with! /// We often /// recommend just using `com.codename1.ui.Container`, `com.codename1.ui.InfiniteContainer` or @@ -127,14 +150,14 @@ /// /// ### MultiList /// -/// ![](https://www.codenameone.com/img/developer-guide/graphics-urlimage-multilist.png) +/// ![](https://www.codenameone.com/developer-guide/img/graphics-urlimage-multilist.png) /// `com.codename1.ui.list.MultiList` a list that is a bit simpler to work with than List `com.codename1.ui.List` although /// our recommendation to use something else still applies /// /// /// ### Slider /// -/// ![](https://www.codenameone.com/img/developer-guide/components-slider.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-slider.png) /// `com.codename1.ui.Slider` allows us to indicate progress or allows the user to drag a bar to /// indicate /// volume (as in quantity) @@ -142,26 +165,26 @@ /// /// ### SwipeableContainer /// -/// ![](https://www.codenameone.com/img/developer-guide/components-swipablecontainer.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-swipablecontainer.png) /// `com.codename1.ui.SwipeableContainer` enables side swipe gesture to expose additional /// functionality /// /// /// ### Tabs /// -/// ![](https://www.codenameone.com/img/developer-guide/components-tabs.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-tabs.png) /// `com.codename1.ui.Tabs` places components/containers into tabbable entries, allows swiping /// between choices thru touch /// /// /// ### Carousel /// -/// ![](https://www.codenameone.com/img/developer-guide/components-tabs-swipe1.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-tabs-swipe1.png) /// `com.codename1.ui.Tabs` can also be used as a swipe carousel /// /// ### TextArea/Field /// -/// ![](https://www.codenameone.com/img/developer-guide/components-text-component.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-text-component.png) /// `com.codename1.ui.TextArea` & `com.codename1.ui.TextField` allow for user input /// via /// the keyboard (virtual or otherwise) @@ -176,34 +199,34 @@ /// /// ### Table /// -/// ![](https://www.codenameone.com/img/developer-guide/components-table-pinstripe.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-table-pinstripe.png) /// `com.codename1.ui.table.Table` displays optionally editable tabular data to the user /// /// ### Tree /// -/// ![](https://www.codenameone.com/img/developer-guide/components-tree-xml.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-tree-xml.png) /// `com.codename1.ui.tree.Tree` displays data in a tree like hierarchy /// /// ### ChartComponent /// -/// ![](https://www.codenameone.com/img/developer-guide/range_bar_chart.png) +/// ![](https://www.codenameone.com/developer-guide/img/range_bar_chart.png) /// `com.codename1.charts.ChartComponent` can embed a wide range of visualization aids and /// animations into your app /// /// /// ### ImageViewer /// -/// ![](https://www.codenameone.com/img/developer-guide/components-imageviewer-dynamic.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-imageviewer-dynamic.png) /// `com.codename1.components.ImageViewer` swipe, pinch to zoom and pan images /// /// ### InfiniteProgress /// -/// ![](https://www.codenameone.com/img/developer-guide/infinite-progress.png) +/// ![](https://www.codenameone.com/developer-guide/img/infinite-progress.png) /// `com.codename1.components.InfiniteProgress` provides a constantly spinning component /// /// ### InteractionDialog /// -/// ![](https://www.codenameone.com/img/developer-guide/components-interaction-dialog.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-interaction-dialog.png) /// `com.codename1.components.InteractionDialog` an "always on top" `com.codename1.ui.Dialog` /// used for non-form modal UI patterns. You can make static `Dialog.show(...)` use /// `InteractionDialog` by default with the theme constant `defaultInteractionDialogModeBool` @@ -212,71 +235,71 @@ /// /// ### MediaPlayer /// -/// ![](https://www.codenameone.com/img/developer-guide/components-mediaplayer.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-mediaplayer.png) /// `com.codename1.components.MediaPlayer` allows playing media including video coupled with the /// `com.codename1.media.MediaManager` /// /// /// ### MultiButton /// -/// ![](https://www.codenameone.com/img/developer-guide/components-multibutton.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-multibutton.png) /// `com.codename1.components.MultiButton` is much more than a button /// /// ### OnOffSwitch /// -/// ![](https://www.codenameone.com/img/developer-guide/components-onoffswitch.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-onoffswitch.png) /// `com.codename1.components.OnOffSwitch` allows us to toggle a state similar to the `com.codename1.ui.CheckBox` /// but with a more modern look /// /// /// ### ShareButton /// -/// ![](https://www.codenameone.com/img/developer-guide/components-sharebutton-android.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-sharebutton-android.png) /// `com.codename1.components.ShareButton` provides native "social share" functionality /// /// ### SpanLabel /// -/// ![](https://www.codenameone.com/img/developer-guide/components-spanlabel.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-spanlabel.png) /// `com.codename1.components.SpanLabel` a text label that "seamlessly" breaks lines /// /// ### SpanButton /// -/// ![](https://www.codenameone.com/img/developer-guide/components-spanbutton.png) +/// ![](https://www.codenameone.com/developer-guide/img/generated/components-spanbutton.png) /// `com.codename1.components.SpanButton` a button that "seamlessly" breaks lines /// /// ### Picker (Date) /// -/// ![](https://www.codenameone.com/img/developer-guide/components-picker-date-android.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-picker-date-android.png) /// `com.codename1.ui.spinner.Picker` allows us to show an OS native picker UI (Date Picker) /// /// /// ### Picker (Time) /// -/// ![](https://www.codenameone.com/img/developer-guide/components-picker-time-android.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-picker-time-android.png) /// `com.codename1.ui.spinner.Picker` allows us to show an OS native picker UI (Time Picker) /// /// /// ### ToastBar /// -/// ![](https://www.codenameone.com/img/developer-guide/components-statusbar.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-statusbar.png) /// `com.codename1.components.ToastBar` shows a non-obtrusive notice on the bottom of the `Form` /// /// /// ### SignatureComponent /// -/// ![](https://www.codenameone.com/img/developer-guide/components-signature2.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-signature2.png) /// `com.codename1.components.SignatureComponent` shows a dialog that allows the user to "sign" /// using the touch screen /// /// /// ### Accordion /// -/// ![](https://www.codenameone.com/img/developer-guide/components-accordion.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-accordion.png) /// `com.codename1.components.Accordion` displays collapsible content panels /// /// ### FloatingHint /// -/// ![](https://www.codenameone.com/img/developer-guide/components-floatinghint.png) +/// ![](https://www.codenameone.com/developer-guide/img/components-floatinghint.png) /// `com.codename1.components.FloatingHint` animates the text field hint into a label on top of /// the text field and visa versa /// @@ -287,3 +310,12 @@ /// `com.codename1.components.FloatingActionButton` hovers over the UI presenting a default /// action package com.codename1.ui; + +// The image URLs above are /developer-guide/img/..., which is where these files +// are actually published: scripts/website/build.sh rsyncs docs/developer-guide/ +// to the site's static/developer-guide/, preserving subdirectories, so a figure +// under img/generated/ serves from /developer-guide/img/generated/. They used to +// be written the other way round, /img/developer-guide/..., a path nothing in +// this repository produces. Renaming or moving a file under docs/developer-guide/img +// therefore does change these URLs, and they have to move with it. + diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava068Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/AppearanceFigure.java similarity index 51% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava068Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/AppearanceFigure.java index 9fffaa8da96..b6957c453a5 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava068Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/AppearanceFigure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * 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 @@ -21,31 +21,31 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; -import com.codename1.ui.*; -import com.codename1.ui.layouts.*; -import com.codename1.ui.table.*; +import com.codename1.ui.Button; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.TextField; +import com.codename1.ui.layouts.BoxLayout; -class TheComponentsOfCodenameOneJava068Snippet { - void snippet() { - // tag::the-components-of-codename-one-java-068[] - Form hi = new Form("Table", new BorderLayout()); - TableModel model = new DefaultTableModel( - new String[] {"Col 1", "Col 2", "Col 3"}, - new Object[][] { - {"Row 1", "Row A", "Row X"}, - {"Row 2", "Row B", "Row Y"}, - {"Row 3", "Row C", "Row Z"}, - {"Row 4", "Row D", "Row K"}, - }) { - public boolean isCellEditable(int row, int col) { - return col != 0; - } - }; - Table table = new Table(model); - hi.add(BorderLayout.CENTER, table); - hi.show(); - // end::the-components-of-codename-one-java-068[] +/// The light and dark pair for the native themes chapter. +/// +/// One form, rendered twice, so the two pictures differ in exactly one thing: +/// the appearance the renderer set before building it. Anything hand-styled +/// here would survive the appearance switch unchanged and quietly weaken that. +public final class AppearanceFigure implements GuideFigure { + @Override + public String id() { + return "native-themes-appearance"; + } + + @Override + public Form build() { + Form form = new Form("Appearance", BoxLayout.y()); + form.add(new Label("Sign in")); + form.add(new TextField("", "Your name")); + form.add(new Button("Continue")); + return form; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/BadgeFloatingButtonFigure.java similarity index 66% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/BadgeFloatingButtonFigure.java index 74118940e74..50107a58017 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/BadgeFloatingButtonFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class TheComponentsOfCodenameOneJava140Snippet { +class BadgeFloatingButtonFigure implements GuideFigure { + Object context; Object url; @@ -78,7 +102,17 @@ class TheComponentsOfCodenameOneJava140Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "badge-floating-button"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::the-components-of-codename-one-java-140[] Form hi = new Form("Badge"); @@ -90,12 +124,13 @@ void snippet() throws Exception { TextField changeBadgeValue = new TextField("33"); changeBadgeValue.addDataChangedListener((i, ii) -> { - badge.setText(changeBadgeValue.getText()); - badge.getParent().revalidate(); + badge.setText(changeBadgeValue.getText()); + badge.getParent().revalidate(); }); hi.add(changeBadgeValue); hi.show(); // end::the-components-of-codename-one-java-140[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ButtonFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ButtonFigure.java new file mode 100644 index 00000000000..492e4d851d6 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ButtonFigure.java @@ -0,0 +1,58 @@ +/* + * 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.Button; +import com.codename1.ui.Form; +import com.codename1.ui.layouts.BoxLayout; + +/// The buttons figure for the Components chapter. +/// +/// Deliberately styled by nothing: every colour, border and font here comes +/// from the native theme the renderer installed, because the point of the +/// picture is what a button looks like on that platform. The chapter shows the +/// iOS and Android renders side by side, which is also what retires its old +/// claim that iOS draws no border on a button. +/// +/// No `FontImage` icon appears here on purpose. A material glyph cannot be +/// byte-reproduced across platforms -- Java2D's antialiased edge coverage +/// differs even at an identical size and origin -- so a figure containing one +/// needs a tolerance sidecar, and this one does not have to. +public final class ButtonFigure implements GuideFigure { + @Override + public String id() { + return "components-button"; + } + + @Override + public Form build() { + Form form = new Form("Buttons", BoxLayout.y()); + form.add(new Button("Press Me")); + form.add(new Button("Another Button")); + Button disabled = new Button("Disabled"); + disabled.setEnabled(false); + form.add(disabled); + return form; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava155Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CheckBoxFigure.java similarity index 50% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava155Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CheckBoxFigure.java index 7481a20369a..75ba718ed13 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava155Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CheckBoxFigure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * 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 @@ -20,76 +20,31 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; -import com.codename1.gpu.*; -import com.codename1.ui.*; -import com.codename1.ui.animations.*; -import com.codename1.ui.events.*; -import com.codename1.ui.geom.*; -import com.codename1.ui.layouts.*; -import com.codename1.ui.list.*; -import com.codename1.ui.plaf.*; -import com.codename1.ui.util.*; -import com.codename1.components.*; -import com.codename1.charts.models.*; -import com.codename1.charts.renderers.*; -import com.codename1.charts.views.*; -import com.codename1.capture.*; -import com.codename1.io.*; -import com.codename1.l10n.*; -import com.codename1.location.*; -import com.codename1.maps.*; -import com.codename1.media.*; -import com.codename1.messaging.*; -import com.codename1.payment.*; -import com.codename1.processing.*; -import com.codename1.properties.*; -import com.codename1.push.*; -import com.codename1.security.*; -import com.codename1.social.*; -import com.codename1.ui.spinner.*; -import java.io.*; -import com.codename1.components.ToastBar.Status; -import com.codename1.maps.layers.*; -import com.codename1.charts.*; -import com.codename1.ui.validation.*; -import com.codename1.xml.*; -import com.codename1.charts.util.*; -import com.codename1.javascript.*; -import com.codename1.ui.tree.*; -import com.codename1.ui.table.*; -import com.codename1.contacts.*; -import java.util.*; +package com.codenameone.developerguide.screenshots; +import com.codename1.ui.ButtonGroup; +import com.codename1.ui.CheckBox; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.RadioButton; +import com.codename1.ui.layouts.BoxLayout; -class TheComponentsOfCodenameOneJava155Snippet { - +/// The check and radio figure for the Components chapter. +/// +/// The tagged region below is what the chapter includes, so the listing beside +/// the picture is the code that drew it. That is the point of moving these out +/// of the compile-only snippet fixtures: a fixture and a screenshot are two +/// sources for one example and drift apart silently. +public final class CheckBoxFigure implements GuideFigure { + @Override + public String id() { + return "components-radiobutton-checkbox"; + } - Object context; - Object url; - Object value; - Object body; - Object event; - String apiKey = "test-key"; - String myHttpsURL = "https://example.com"; - java.util.List validKeysList = new java.util.ArrayList<>(); - Image myImage; - Graphics graphics; - Graphics g; - GraphicsDevice device; - Form form; - Form hi; - Container cnt; - Container myForm; - Component component; - Button button; - MultiButton myMultiButton; - Label label; - BrowserComponent browserComponent; - Resources theme; - - void snippet() throws Exception { + @Override + public Form build() { // tag::the-components-of-codename-one-java-155[] Form hi = new Form("CheckBox", new BoxLayout(BoxLayout.Y_AXIS)); Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); @@ -108,7 +63,6 @@ void snippet() throws Exception { hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3); hi.show(); // end::the-components-of-codename-one-java-155[] + return hi; } - - } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava146Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLabelTextPositionFigure.java similarity index 70% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava146Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLabelTextPositionFigure.java index dd24a8dc60a..b24b7a9c5cb 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava146Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLabelTextPositionFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -20,7 +43,7 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -63,7 +86,8 @@ import java.util.*; -class TheComponentsOfCodenameOneJava146Snippet { +class ComponentsLabelTextPositionFigure implements GuideFigure { + Object context; @@ -89,7 +113,17 @@ class TheComponentsOfCodenameOneJava146Snippet { BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "components-label-text-position"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::the-components-of-codename-one-java-146[] Form hi = new Form("Label Position", new BoxLayout(BoxLayout.Y_AXIS)); Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); @@ -104,7 +138,6 @@ void snippet() throws Exception { hi.add(left).add(right).add(bottom).add(top); hi.show(); // end::the-components-of-codename-one-java-146[] + return hi; } - - } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLinkButtonFigure.java similarity index 66% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLinkButtonFigure.java index 162e2db455e..d31fd93606d 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLinkButtonFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class TheComponentsOfCodenameOneJava031Snippet { +class ComponentsLinkButtonFigure implements GuideFigure { + Object context; Object url; @@ -78,7 +102,17 @@ class TheComponentsOfCodenameOneJava031Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "components-link-button"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::the-components-of-codename-one-java-031[] Form hi = new Form("Button"); Button b = new Button("Link Button"); @@ -87,5 +121,6 @@ void snippet() throws Exception { hi.add(b); b.addActionListener((e) -> Log.p("Clicked")); // end::the-components-of-codename-one-java-031[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava159Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsSpanbuttonFigure.java similarity index 69% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava159Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsSpanbuttonFigure.java index bfe5eae4577..7dd284fb3d9 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava159Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsSpanbuttonFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -20,7 +43,7 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -63,7 +86,8 @@ import java.util.*; -class TheComponentsOfCodenameOneJava159Snippet { +class ComponentsSpanbuttonFigure implements GuideFigure { + Object context; @@ -89,7 +113,17 @@ class TheComponentsOfCodenameOneJava159Snippet { BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "components-spanbutton"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::the-components-of-codename-one-java-159[] Form hi = new Form("SpanButton", new BoxLayout(BoxLayout.Y_AXIS)); Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); @@ -98,7 +132,6 @@ void snippet() throws Exception { hi.add(sb); hi.show(); // end::the-components-of-codename-one-java-159[] + return hi; } - - } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableFigure.java new file mode 100644 index 00000000000..6444361205f --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableFigure.java @@ -0,0 +1,86 @@ +/* + * 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. + */ + +/* + * Copyright (c) 2012, 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.table.*; + +class ComponentsTableFigure implements GuideFigure { + + + + @Override + public String id() { + return "components-table"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { + // tag::the-components-of-codename-one-java-068[] + Form hi = new Form("Table", new BorderLayout()); + TableModel model = new DefaultTableModel( + new String[] {"Col 1", "Col 2", "Col 3"}, + new Object[][] { + {"Row 1", "Row A", "Row X"}, + {"Row 2", "Row B", "Row Y"}, + {"Row 3", "Row C", "Row Z"}, + {"Row 4", "Row D", "Row K"}, + }) { + public boolean isCellEditable(int row, int col) { + return col != 0; + } + }; + Table table = new Table(model); + hi.add(BorderLayout.CENTER, table); + hi.show(); + // end::the-components-of-codename-one-java-068[] + return hi; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTablePinstripeFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTablePinstripeFigure.java new file mode 100644 index 00000000000..ca8ae568f6d --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTablePinstripeFigure.java @@ -0,0 +1,119 @@ +/* + * 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. + */ + +/* + * Copyright (c) 2012, 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.spinner.*; +import com.codename1.ui.table.*; + +class ComponentsTablePinstripeFigure implements GuideFigure { + + + + @Override + public String id() { + return "components-table-pinstripe"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { + // tag::the-components-of-codename-one-java-070[] + Form hi = new Form("Table", new BorderLayout()); + TableModel model = new DefaultTableModel( + new String[] {"Col 1", "Col 2", "Col 3"}, + new Object[][] { + {"Row 1", "Row A", "Row X"}, + {"Row 2", "Row B can now stretch", null}, + {"Row 3", "Row C", "Row Z"}, + {"Row 4", "Row D", "Row K"}, + }) { + public boolean isCellEditable(int row, int col) { + return col != 0; + } + }; + Table table = new Table(model) { + @Override + protected Component createCell(Object value, int row, int column, boolean editable) { + Component cell; + if (row == 1 && column == 1) { + Picker p = new Picker(); + p.setType(Display.PICKER_TYPE_STRINGS); + p.setStrings("Row B can now stretch", "This is a good value", + "So Is This", "Better than text field"); + p.setSelectedString((String)value); + p.setUIID("TableCell"); + p.addActionListener(e -> getModel().setValueAt(row, column, p.getSelectedString())); + cell = p; + } else { + cell = super.createCell(value, row, column, editable); + } + if (row > -1 && row % 2 == 0) { + cell.getAllStyles().setBgColor(0xeeeeee); + cell.getAllStyles().setBgTransparency(255); + } + return cell; + } + + @Override + protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { + TableLayout.Constraint con = super.createCellConstraint(value, row, column); + if (row == 1 && column == 1) { + con.setHorizontalSpan(2); + } + con.setWidthPercentage(33); + return con; + } + }; + hi.add(BorderLayout.CENTER, table); + hi.show(); + // end::the-components-of-codename-one-java-070[] + return hi; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableWithSpanningFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableWithSpanningFigure.java new file mode 100644 index 00000000000..dd5b8d4352b --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableWithSpanningFigure.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +/* + * Copyright (c) 2012, 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.table.*; + +class ComponentsTableWithSpanningFigure implements GuideFigure { + + + + @Override + public String id() { + return "components-table-with-spanning"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { + // tag::the-components-of-codename-one-java-069[] + Form hi = new Form("Table", new BorderLayout()); + TableModel model = new DefaultTableModel( + new String[] {"Col 1", "Col 2", "Col 3"}, + new Object[][] { + {"Row 1", "Row A", "Row X"}, + {"Row 2", "Row B can now stretch", null}, + {"Row 3", "Row C", "Row Z"}, + {"Row 4", "Row D", "Row K"}, + }) { + public boolean isCellEditable(int row, int col) { + return col != 0; + } + }; + Table table = new Table(model) { + @Override + protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { + TableLayout.Constraint con = super.createCellConstraint(value, row, column); + if (row == 1 && column == 1) { + con.setHorizontalSpan(2); + } + con.setWidthPercentage(33); + return con; + } + }; + hi.add(BorderLayout.CENTER, table); + hi.show(); + // end::the-components-of-codename-one-java-069[] + return hi; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava158Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsToolbarFigure.java similarity index 58% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava158Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsToolbarFigure.java index 34a7d1bafb0..82c41cb573d 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava158Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsToolbarFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -20,7 +43,7 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -63,7 +86,8 @@ import java.util.*; -class TheComponentsOfCodenameOneJava158Snippet { +class ComponentsToolbarFigure implements GuideFigure { + Object context; @@ -89,47 +113,29 @@ class TheComponentsOfCodenameOneJava158Snippet { BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { - // tag::the-components-of-codename-one-java-158[] - Form hi = new Form("MultiButton", new BoxLayout(BoxLayout.Y_AXIS)); - Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); - Image emblem = FontImage.createMaterial(FontImage.MATERIAL_STAR, "Label", 2.0f); - MultiButton twoLinesNoIcon = new MultiButton("MultiButton"); - twoLinesNoIcon.setTextLine2("Line 2"); - MultiButton oneLineIconEmblem = new MultiButton("Icon + Emblem"); - oneLineIconEmblem.setIcon(icon); - oneLineIconEmblem.setEmblem(emblem); - MultiButton twoLinesIconEmblem = new MultiButton("Icon + Emblem"); - twoLinesIconEmblem.setIcon(icon); - twoLinesIconEmblem.setEmblem(emblem); - twoLinesIconEmblem.setTextLine2("Line 2"); - - MultiButton twoLinesIconEmblemHorizontal = new MultiButton("Icon + Emblem"); - twoLinesIconEmblemHorizontal.setIcon(icon); - twoLinesIconEmblemHorizontal.setEmblem(emblem); - twoLinesIconEmblemHorizontal.setTextLine2("Line 2 Horizontal"); - twoLinesIconEmblemHorizontal.setHorizontalLayout(true); + - MultiButton twoLinesIconCheckBox = new MultiButton("CheckBox"); - twoLinesIconCheckBox.setIcon(icon); - twoLinesIconCheckBox.setCheckBox(true); - twoLinesIconCheckBox.setTextLine2("Line 2"); + @Override + public String id() { + return "components-toolbar"; + } - MultiButton fourLinesIcon = new MultiButton("With Icon"); - fourLinesIcon.setIcon(icon); - fourLinesIcon.setTextLine2("Line 2"); - fourLinesIcon.setTextLine3("Line 3"); - fourLinesIcon.setTextLine4("Line 4"); + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { + // tag::the-components-of-codename-one-java-191[] + Style s = UIManager.getInstance().getComponentStyle("TitleCommand"); + Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, s); + Toolbar.setGlobalToolbar(true); - hi.add(oneLineIconEmblem). - add(twoLinesNoIcon). - add(twoLinesIconEmblem). - add(twoLinesIconEmblemHorizontal). - add(twoLinesIconCheckBox). - add(fourLinesIcon); + Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS)); + hi.getToolbar().addCommandToLeftBar("Left", icon, (e) -> Log.p("Clicked")); + hi.getToolbar().addCommandToRightBar("Right", icon, (e) -> Log.p("Clicked")); + hi.getToolbar().addCommandToOverflowMenu("Overflow", icon, (e) -> Log.p("Clicked")); + hi.getToolbar().addCommandToSideMenu("Sidemenu", icon, (e) -> Log.p("Clicked")); hi.show(); - // end::the-components-of-codename-one-java-158[] + // end::the-components-of-codename-one-java-191[] + return hi; } - - } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava113Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CsvParsingFigure.java similarity index 67% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava113Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CsvParsingFigure.java index 39bc3b44fcd..d24b85e2f1b 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava113Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/CsvParsingFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -20,7 +43,7 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -64,7 +87,8 @@ import com.codename1.io.CharArrayReader; -class IoJava113Snippet { +class CsvParsingFigure implements GuideFigure { + Object context; @@ -106,22 +130,33 @@ class IoJava113Snippet { java.io.InputStream input; java.io.OutputStream outputStream; - void snippet() throws Exception { + + + @Override + public String id() { + return "csv-parsing"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::io-java-113[] Form hi = new Form("CSV Parsing", new BorderLayout()); CSVParser parser = new CSVParser(); try(Reader r = new CharArrayReader("1997,Ford,E350,\"Super, \"\"luxurious\"\" truck\"".toCharArray());) { - String[][] data = parser.parse(r); - String[] columnNames = new String[data[0].length]; - for(int iter= 0 ; iter < columnNames.length ; iter++) { - columnNames[iter] = "Col " + (iter + 1); - } - TableModel tm = new DefaultTableModel(columnNames, data); - hi.add(BorderLayout.CENTER, new Table(tm)); + String[][] data = parser.parse(r); + String[] columnNames = new String[data[0].length]; + for(int iter= 0 ; iter < columnNames.length ; iter++) { + columnNames[iter] = "Col " + (iter + 1); + } + TableModel tm = new DefaultTableModel(columnNames, data); + hi.add(BorderLayout.CENTER, new Table(tm)); } catch(IOException err) { - Log.e(err); + Log.e(err); } hi.show(); // end::io-java-113[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureDevice.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureDevice.java new file mode 100644 index 00000000000..ca2df8335f7 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureDevice.java @@ -0,0 +1,90 @@ +/* + * 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.codenameone.developerguide.screenshots; + +/// The render targets for guide figures. +/// +/// Geometry comes from `scripts/skindesigner/common/src/main/resources/devices.json` +/// rather than being invented here, and the entry each profile is taken from is +/// named so it can be checked. Figures render at the device's LOGICAL +/// resolution with no skin. A skin would frame every picture in a phone bezel, +/// and it would also set `isIOS` in `JavaSEPort.loadTrueTypeFont`, which +/// resolves `native:` fonts against host-installed faces instead of the port's +/// bundled Roboto -- making the output depend on the machine that produced it. +/// +/// `pixelMilliRatio` is logical pixels per millimetre, `ppi / scale / 25.4`. +/// Both profiles land near 6.3, the 160dpi baseline the dip unit is defined +/// against, so a dip is about a pixel here. +public enum FigureDevice { + /// "Apple iPhone 16": 1179x2556 at scale 3.0, so 393x852 logical, 460ppi. + IOS("ios", 393, 852, 460.0 / 3.0 / 25.4, "/iOSModernTheme.res"), + + /// "Google Pixel 9": 1080x2424 at scale 2.638, so 409x919 logical, 422ppi. + ANDROID("android", 409, 919, 422.0 / 2.638 / 25.4, "/AndroidMaterialTheme.res"); + + private final String key; + private final int width; + private final int height; + private final double pixelMilliRatio; + private final String themeResource; + + FigureDevice(String key, int width, int height, double pixelMilliRatio, String themeResource) { + this.key = key; + this.width = width; + this.height = height; + this.pixelMilliRatio = pixelMilliRatio; + this.themeResource = themeResource; + } + + /// The name used on the command line and in generated file names. + public String key() { + return key; + } + + public int width() { + return width; + } + + public int height() { + return height; + } + + public double pixelMilliRatio() { + return pixelMilliRatio; + } + + /// Classpath resource of the native theme, shipped inside `codenameone-javase`. + public String themeResource() { + return themeResource; + } + + public static FigureDevice fromKey(String value) { + for (FigureDevice d : values()) { + if (d.key.equals(value)) { + return d; + } + } + throw new IllegalArgumentException("Unknown figure device: " + value); + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureVariant.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureVariant.java new file mode 100644 index 00000000000..583ebea5080 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/FigureVariant.java @@ -0,0 +1,61 @@ +/* + * 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.codenameone.developerguide.screenshots; + +/// One figure rendered on one device in one appearance, and the file it lands in. +/// +/// Variants are declared rather than generated combinatorially, because every +/// generated image has to be referenced by a chapter -- `find_unused_images.py` +/// fails the build for one that is not -- so producing a whole matrix would +/// break the build for the pictures no chapter had asked for yet. +public final class FigureVariant { + private final GuideFigure figure; + private final FigureDevice device; + private final boolean darkMode; + private final String fileName; + + public FigureVariant(GuideFigure figure, FigureDevice device, boolean darkMode, String fileName) { + this.figure = figure; + this.device = device; + this.darkMode = darkMode; + this.fileName = fileName; + } + + public GuideFigure figure() { + return figure; + } + + public FigureDevice device() { + return device; + } + + public boolean darkMode() { + return darkMode; + } + + /// File name written into the output directory, including the extension. + public String fileName() { + return fileName; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageFixedFigure.java similarity index 65% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageFixedFigure.java index 4f3429c1494..68c28b0b1ee 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageFixedFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class GraphicsJava032Snippet { +class GraphicsFontimageFixedFigure implements GuideFigure { + Object context; Object url; @@ -78,7 +102,17 @@ class GraphicsJava032Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "graphics-fontimage-fixed"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::graphics-java-032[] Form hi = new Form("Icon Font"); Font materialFont = FontImage.getMaterialDesignFont(); @@ -87,5 +121,6 @@ void snippet() throws Exception { hi.add(fntImage); hi.show(); // end::graphics-java-032[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageMaterialFigure.java similarity index 65% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageMaterialFigure.java index f53cd219b55..e27df29d86d 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageMaterialFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class GraphicsJava034Snippet { +class GraphicsFontimageMaterialFigure implements GuideFigure { + Object context; Object url; @@ -78,12 +102,23 @@ class GraphicsJava034Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "graphics-fontimage-material"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::graphics-java-034[] Form hi = new Form("Icon Font"); Button myButton = new Button("Save"); myButton.setIcon(FontImage.createMaterial(FontImage.MATERIAL_SAVE, myButton.getUnselectedStyle())); hi.add(myButton); // end::graphics-java-034[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageStyleFigure.java similarity index 66% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageStyleFigure.java index 583f8cf9fcb..24391c04f97 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageStyleFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class GraphicsJava033Snippet { +class GraphicsFontimageStyleFigure implements GuideFigure { + Object context; Object url; @@ -78,7 +102,17 @@ class GraphicsJava033Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "graphics-fontimage-style"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::graphics-java-033[] Form hi = new Form("Icon Font"); Font materialFont = FontImage.getMaterialDesignFont(); @@ -89,5 +123,6 @@ void snippet() throws Exception { hi.add(myButton); hi.show(); // end::graphics-java-033[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsGlasspaneFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsGlasspaneFigure.java new file mode 100644 index 00000000000..c1ca5d4a1d0 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsGlasspaneFigure.java @@ -0,0 +1,137 @@ +/* + * 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. + */ + +/* + * Copyright (c) 2012, 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.codenameone.developerguide.screenshots; + +import com.codename1.gpu.*; +import com.codename1.ui.*; +import com.codename1.ui.animations.*; +import com.codename1.ui.events.*; +import com.codename1.ui.geom.*; +import com.codename1.ui.layouts.*; +import com.codename1.ui.list.*; +import com.codename1.ui.plaf.*; +import com.codename1.ui.util.*; +import com.codename1.components.*; +import com.codename1.charts.models.*; +import com.codename1.charts.renderers.*; +import com.codename1.charts.views.*; +import com.codename1.capture.*; +import com.codename1.io.*; +import com.codename1.l10n.*; +import com.codename1.location.*; +import com.codename1.maps.*; +import com.codename1.media.*; +import com.codename1.messaging.*; +import com.codename1.payment.*; +import com.codename1.processing.*; +import com.codename1.properties.*; +import com.codename1.push.*; +import com.codename1.security.*; +import com.codename1.social.*; +import com.codename1.ui.spinner.*; +import java.io.*; +import java.util.*; + + +class GraphicsGlasspaneFigure implements GuideFigure { + + + Object context; + Object url; + Object value; + Object body; + Object event; + String apiKey = "test-key"; + String myHttpsURL = "https://example.com"; + java.util.List validKeysList = new java.util.ArrayList<>(); + Image myImage; + Graphics graphics; + Graphics g; + GraphicsDevice device; + Form form; + Form hi; + Container cnt; + Container myForm; + Component component; + Button button; + MultiButton myMultiButton; + Label label; + BrowserComponent browserComponent; + Resources theme; + + + @Override + public String id() { + return "graphics-glasspane"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { + // tag::graphics-java-006[] + Form hi = new Form("Glass Pane", new BoxLayout(BoxLayout.Y_AXIS)); + Style s = UIManager.getInstance().getComponentStyle("Label"); + s.setFgColor(0xff0000); + s.setBgTransparency(0); + Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s).toImage(); + TextField tf1 = new TextField("My Field"); + tf1.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS); + tf1.getAllStyles().setMargin(5, 5, 5, 5); + hi.add(tf1); + hi.setGlassPane((g, rect) -> { + int x = tf1.getAbsoluteX() + tf1.getWidth(); + int y = tf1.getAbsoluteY(); + x -= warningImage.getWidth() / 2; + y += (tf1.getHeight() / 2 - warningImage.getHeight() / 2); + g.drawImage(warningImage, x, y); + }); + hi.show(); + // end::graphics-java-006[] + return hi; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsHiworldFigure.java similarity index 60% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsHiworldFigure.java index 61e1b757f5a..459e79ffb32 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsHiworldFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class GraphicsJava001Snippet { +class GraphicsHiworldFigure implements GuideFigure { + Object context; Object url; @@ -78,25 +102,36 @@ class GraphicsJava001Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "graphics-hiworld"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::graphics-java-001[] // hide the title Form hi = new Form("", new BorderLayout()); hi.add(BorderLayout.CENTER, new Component() { - @Override - public void paint(Graphics g) { - // red color - g.setColor(0xff0000); + @Override + public void paint(Graphics g) { + // red color + g.setColor(0xff0000); - // paint the screen in red - g.fillRect(getX(), getY(), getWidth(), getHeight()); + // paint the screen in red + g.fillRect(getX(), getY(), getWidth(), getHeight()); - // draw hi world in white text in the top left corner of the screen - g.setColor(0xffffff); - g.drawString("Hi World", getX(), getY()); - } + // draw hi world in white text in the top left corner of the screen + g.setColor(0xffffff); + g.drawString("Hi World", getX(), getY()); + } }); hi.show(); // end::graphics-java-001[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigure.java new file mode 100644 index 00000000000..a408a7356e9 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigure.java @@ -0,0 +1,44 @@ +/* + * 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.Form; + +/// One picture in the developer guide, expressed as the code that produces it. +/// +/// A figure builds a [Form] and returns it. It may show the form -- several +/// figures carry the chapter's own sample verbatim, and those samples end in +/// `show()` because that is what a reader would write -- but it must not depend +/// on having shown it. The renderer owns the window size, the theme and the +/// appearance, and prepares the returned form at the target geometry either +/// way, so one figure renders on several devices and in both appearances. +public interface GuideFigure { + /// Stable identity used to name the output file. Lower case with dashes, + /// matching the image naming already used under `docs/developer-guide/img`. + String id(); + + /// Builds the form to photograph. Called on the Codename One EDT with the + /// theme and appearance already installed. + Form build(); +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigureRenderer.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigureRenderer.java new file mode 100644 index 00000000000..c52c8c7a136 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigureRenderer.java @@ -0,0 +1,228 @@ +/* + * 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.codenameone.developerguide.screenshots; + +import com.codename1.ui.CN; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import com.codename1.ui.Toolbar; +import com.codename1.ui.plaf.UIManager; +import com.codename1.ui.util.ImageIO; +import com.codename1.ui.util.Resources; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Hashtable; + +/// Renders [GuideFigure]s under a native theme at a device profile. +/// +/// This is deliberately separate from `PreAdvancedThemingScreenshots`, which +/// draws the schematic layout diagrams. Those are green labelled blocks whose +/// job is to show where a layout manager puts things, and rendering them under +/// a real theme would bury that teaching point under component chrome. They +/// keep their hand-rolled styling and their own pass; this class is for figures +/// whose subject is a real interface. +public final class GuideFigureRenderer { + private GuideFigureRenderer() { + } + + /// Where a rendered figure is written. Same shape as the sink the + /// schematic generator uses, so one output directory holds both passes. + public interface ScreenshotSink { + OutputStream open(String fileName) throws IOException; + } + + /// Renders every variant belonging to `device`. + /// + /// The theme is installed once per call. Appearance is not: dark mode is + /// set per variant, each time followed by `UIManager.refreshTheme()`, + /// without which the styles stay cached at their light values and every + /// dark frame silently renders light. + public static void render(ScreenshotSink sink, FigureDevice device, FigureVariant[] variants) + throws IOException { + assertNoSkinIsActive(); + installTheme(device.themeResource()); + + StringBuilder failures = new StringBuilder(); + int rendered = 0; + for (FigureVariant variant : variants) { + if (variant.device() != device) { + continue; + } + try { + Display.getInstance().setDarkMode(Boolean.valueOf(variant.darkMode())); + UIManager.getInstance().refreshTheme(); + + // Figures are built one after another in one process, so anything + // a sample leaves set globally lands on every figure after it and + // the output starts depending on the order of the manifest. The + // toolbar sample calls Toolbar.setGlobalToolbar(true), which gave + // a toolbar to the nine figures registered below it whose own + // samples never asked for one. Snapshot and restore rather than + // patching that one sample, because the next such flag would + // otherwise repeat this silently. + boolean globalToolbar = Toolbar.isGlobalToolbar(); + Form form; + try { + form = variant.figure().build(); + } finally { + Toolbar.setGlobalToolbar(globalToolbar); + } + prepare(form, device); + verifyAppearance(variant, form); + write(sink, variant.fileName(), form, device); + rendered++; + } catch (Throwable err) { + // One figure that cannot render must not take the rest with it: + // the run reports every failure and then fails, which beats + // stopping at the first and rediscovering the next one build + // after build. + failures.append("\n ").append(variant.fileName()).append(": ") + .append(err.getClass().getName()).append(": ").append(err.getMessage()); + } + } + System.out.println("Rendered " + rendered + " figure(s) for " + device.key()); + if (failures.length() > 0) { + throw new IOException("figures failed to render:" + failures); + } + } + + /// A skin puts the host machine's fonts back into the output, which is the + /// one difference that makes a figure impossible to regenerate elsewhere. + /// Nothing here loads one today; this fails loudly if that ever changes. + private static void assertNoSkinIsActive() { + if ("ios".equals(Display.getInstance().getPlatformName())) { + throw new IllegalStateException( + "a skin is active, so native: fonts would resolve to installed system " + + "faces and these figures would stop being reproducible off this machine"); + } + } + + private static void installTheme(String resourcePath) throws IOException { + Resources res = Resources.open(resourcePath); + String[] names = res.getThemeResourceNames(); + if (names == null || names.length == 0) { + throw new IllegalStateException("Theme resource " + resourcePath + " contains no themes"); + } + Hashtable themeProps = res.getTheme(names[0]); + UIManager.getInstance().setThemeProps(themeProps); + UIManager.getInstance().refreshTheme(); + } + + /// Checks the appearance actually took, rather than trusting the file name. + /// + /// A dark render that comes out light is the most expensive failure + /// available here, because it is invisible in review and repeats across + /// every figure. The port has produced exactly that before, so the dark + /// frames are required to differ from the theme's light background rather + /// than merely to have been asked for. + private static void verifyAppearance(FigureVariant variant, Form form) { + Boolean reported = Display.getInstance().isDarkMode(); + if (reported == null || reported.booleanValue() != variant.darkMode()) { + throw new IllegalStateException(variant.fileName() + ": asked for darkMode=" + + variant.darkMode() + " but Display reports " + reported); + } + // Only the dark direction is asserted. The failure worth catching is a + // dark render that silently comes out light, which repeats across every + // figure and is invisible in review. The converse is not a failure at + // all: a figure is free to set a dark background of its own in light + // mode, and the image masking figure does exactly that -- a red form, + // which a luminance test reads as "dark" and which this used to reject. + if (variant.darkMode() && isLight(form.getStyle().getBgColor())) { + throw new IllegalStateException(variant.fileName() + + ": dark mode is set but the form background is " + + Integer.toHexString(form.getStyle().getBgColor()) + + ", which is a light colour"); + } + } + + private static boolean isLight(int rgb) { + int r = (rgb >> 16) & 0xff; + int g = (rgb >> 8) & 0xff; + int b = rgb & 0xff; + return (r * 299 + g * 587 + b * 114) / 1000 > 127; + } + + private static void prepare(Form form, FigureDevice device) { + CN.setWindowSize(device.width(), device.height()); + form.setScrollableY(false); + form.show(); + form.setWidth(device.width()); + form.setHeight(device.height()); + form.getContentPane().setWidth(device.width()); + form.getContentPane().setHeight( + Math.max(0, device.height() - form.getTitleArea().getHeight())); + form.revalidate(); + } + + /// Height to keep, so a figure is the part of the screen that has something + /// on it rather than a phone-shaped picture that is four fifths empty. + /// + /// Two shapes have to work at once, and each breaks the obvious reading of + /// the other. prepare() sizes the content pane to the whole device, so a + /// child in BorderLayout.CENTER is stretched to fill it: trusting its laid-out + /// height cropped nothing at all and left the table and tree figures 409x917 + /// with a blank page below the content. But a component that paints itself and + /// declares no intrinsic size -- the custom Component behind the Hi World + /// figure -- reports a preferred height of zero while legitimately filling the + /// whole viewport, and trusting THAT cropped it to a 26-pixel sliver of its + /// red canvas. + /// + /// So: a preferred height is used when the component states one, and a + /// component that states none is taken at the height it was given. + private static int figureHeight(Form form, FigureDevice device) { + Container content = form.getContentPane(); + int bottom = 0; + for (int i = 0; i < content.getComponentCount(); i++) { + Component child = content.getComponentAt(i); + int preferred = child.getPreferredH(); + int height = preferred > 0 ? preferred : child.getHeight(); + bottom = Math.max(bottom, child.getY() + height); + } + // No children at all means the figure IS the title area -- the toolbar + // figures are exactly that -- so the crop stops just below it rather than + // falling back to the content pane's stretched height, which produced a + // toolbar with a blank page underneath. + int used = content.getAbsoluteY() + bottom + content.getStyle().getPaddingBottom(); + return Math.min(device.height(), Math.max(1, used)); + } + + private static void write(ScreenshotSink sink, String fileName, Form form, FigureDevice device) + throws IOException { + Image screenshot = Image.createImage(device.width(), device.height(), 0xffffff); + Graphics graphics = screenshot.getGraphics(); + form.paintComponent(graphics, true); + screenshot = screenshot.subImage(0, 0, device.width(), figureHeight(form, device), true); + OutputStream out = sink.open(fileName); + try { + ImageIO.getImageIO().save(screenshot, out, ImageIO.FORMAT_PNG, 1); + } finally { + out.close(); + } + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigures.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigures.java new file mode 100644 index 00000000000..51b29e7749c --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/GuideFigures.java @@ -0,0 +1,82 @@ +/* + * 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.codenameone.developerguide.screenshots; + +/// The figures this repository generates, and the file each one lands in. +/// +/// This list is the manifest. The workflow compares the generated directory +/// against `docs/developer-guide/img` without being told how many files to +/// expect, so adding an entry here and referencing the image from a chapter is +/// the whole of adding a figure. +public final class GuideFigures { + private GuideFigures() { + } + + public static FigureVariant[] variants() { + GuideFigure button = new ButtonFigure(); + GuideFigure appearance = new AppearanceFigure(); + return new FigureVariant[] { + // The Components chapter shows the platforms side by side. + new FigureVariant(button, FigureDevice.IOS, false, "components-button-ios.png"), + new FigureVariant(button, FigureDevice.ANDROID, false, "components-button-android.png"), + // The native themes chapter shows one platform in both appearances. + new FigureVariant(appearance, FigureDevice.ANDROID, false, "native-themes-appearance-light.png"), + new FigureVariant(appearance, FigureDevice.ANDROID, true, "native-themes-appearance-dark.png"), + // One canonical variant where the component, not the platform, is + // the subject -- and Android Material for it, because these render + // through the port's bundled Roboto, which is what Material 3 + // actually specifies. An iOS Modern render is typographically wrong + // for the same reason: it comes out in Roboto rather than SF, so it + // is worth having only where the platform difference is the point. + new FigureVariant(new CheckBoxFigure(), FigureDevice.ANDROID, false, + "components-radiobutton-checkbox.png"), + new FigureVariant(new MultiButtonFigure(), FigureDevice.ANDROID, false, + "components-multibutton.png"), + new FigureVariant(new SpanLabelFigure(), FigureDevice.ANDROID, false, + "components-spanlabel.png"), + new FigureVariant(new L10nBasicFigure(), FigureDevice.ANDROID, false, "l10n-basic.png"), + new FigureVariant(new ComponentsLabelTextPositionFigure(), FigureDevice.ANDROID, false, "components-label-text-position.png"), + new FigureVariant(new ComponentsLinkButtonFigure(), FigureDevice.ANDROID, false, "components-link-button.png"), + // Toggle styling leans on the same theme support as ComponentGroup: on + // Android Material the converted buttons come back as plain rows, + // indistinguishable from the ordinary checkbox and radio figure. + // ComponentGroup does nothing unless the theme sets ComponentGroupBool, which + // the iOS theme does by default and Android Material does not -- rendered on + // Android the sample comes out as plain separate rows, which is the opposite + // of what the section demonstrates. + new FigureVariant(new ComponentsSpanbuttonFigure(), FigureDevice.ANDROID, false, "components-spanbutton.png"), + new FigureVariant(new ComponentsTableFigure(), FigureDevice.ANDROID, false, "components-table.png"), + new FigureVariant(new ComponentsTableWithSpanningFigure(), FigureDevice.ANDROID, false, "components-table-with-spanning.png"), + new FigureVariant(new ComponentsTablePinstripeFigure(), FigureDevice.ANDROID, false, "components-table-pinstripe.png"), + new FigureVariant(new ComponentsToolbarFigure(), FigureDevice.ANDROID, false, "components-toolbar.png"), + new FigureVariant(new BadgeFloatingButtonFigure(), FigureDevice.ANDROID, false, "badge-floating-button.png"), + new FigureVariant(new GraphicsHiworldFigure(), FigureDevice.IOS, false, "graphics-hiworld.png"), + new FigureVariant(new GraphicsGlasspaneFigure(), FigureDevice.ANDROID, false, "graphics-glasspane.png"), + new FigureVariant(new GraphicsFontimageFixedFigure(), FigureDevice.ANDROID, false, "graphics-fontimage-fixed.png"), + new FigureVariant(new GraphicsFontimageStyleFigure(), FigureDevice.ANDROID, false, "graphics-fontimage-style.png"), + new FigureVariant(new GraphicsFontimageMaterialFigure(), FigureDevice.ANDROID, false, "graphics-fontimage-material.png"), + new FigureVariant(new CsvParsingFigure(), FigureDevice.ANDROID, false, "csv-parsing.png"), + }; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/L10nBasicFigure.java similarity index 66% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/L10nBasicFigure.java index f37ed132c3c..68bb88ebf3c 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/L10nBasicFigure.java @@ -1,3 +1,26 @@ +/* + * 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. + */ + /* * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -21,7 +44,7 @@ * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; +package com.codenameone.developerguide.screenshots; import com.codename1.gpu.*; import com.codename1.ui.*; @@ -54,7 +77,8 @@ import java.util.*; -class MiscellaneousFeaturesJava016Snippet { +class L10nBasicFigure implements GuideFigure { + Object context; Object url; @@ -78,7 +102,17 @@ class MiscellaneousFeaturesJava016Snippet { Label label; BrowserComponent browserComponent; Resources theme; - void snippet() throws Exception { + + + @Override + public String id() { + return "l10n-basic"; + } + + /// The tagged region is what the chapter includes, so the listing beside the + /// picture is the code that drew it. + @Override + public Form build() { // tag::miscellaneous-features-java-016[] Form hi = new Form("L10N", new BoxLayout(BoxLayout.Y_AXIS)); HashMap resourceBudle = new HashMap(); @@ -87,5 +121,6 @@ void snippet() throws Exception { hi.add(new Label("Localize")); hi.show(); // end::miscellaneous-features-java-016[] + return hi; } } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/MultiButtonFigure.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/MultiButtonFigure.java new file mode 100644 index 00000000000..f6b82010ac8 --- /dev/null +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/MultiButtonFigure.java @@ -0,0 +1,87 @@ +/* + * 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.codenameone.developerguide.screenshots; + +import com.codename1.components.MultiButton; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.layouts.BoxLayout; + +/// The MultiButton figure for the Components chapter. +/// +/// The tagged region below is what the chapter includes, so the listing beside +/// the picture is the code that drew it. That is the point of moving these out +/// of the compile-only snippet fixtures: a fixture and a screenshot are two +/// sources for one example and drift apart silently. +public final class MultiButtonFigure implements GuideFigure { + @Override + public String id() { + return "components-multibutton"; + } + + @Override + public Form build() { + // tag::the-components-of-codename-one-java-158[] + Form hi = new Form("MultiButton", new BoxLayout(BoxLayout.Y_AXIS)); + Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); + Image emblem = FontImage.createMaterial(FontImage.MATERIAL_STAR, "Label", 2.0f); + MultiButton twoLinesNoIcon = new MultiButton("MultiButton"); + twoLinesNoIcon.setTextLine2("Line 2"); + MultiButton oneLineIconEmblem = new MultiButton("Icon + Emblem"); + oneLineIconEmblem.setIcon(icon); + oneLineIconEmblem.setEmblem(emblem); + MultiButton twoLinesIconEmblem = new MultiButton("Icon + Emblem"); + twoLinesIconEmblem.setIcon(icon); + twoLinesIconEmblem.setEmblem(emblem); + twoLinesIconEmblem.setTextLine2("Line 2"); + + MultiButton twoLinesIconEmblemHorizontal = new MultiButton("Icon + Emblem"); + twoLinesIconEmblemHorizontal.setIcon(icon); + twoLinesIconEmblemHorizontal.setEmblem(emblem); + twoLinesIconEmblemHorizontal.setTextLine2("Line 2 Horizontal"); + twoLinesIconEmblemHorizontal.setHorizontalLayout(true); + + MultiButton twoLinesIconCheckBox = new MultiButton("CheckBox"); + twoLinesIconCheckBox.setIcon(icon); + twoLinesIconCheckBox.setCheckBox(true); + twoLinesIconCheckBox.setTextLine2("Line 2"); + + MultiButton fourLinesIcon = new MultiButton("With Icon"); + fourLinesIcon.setIcon(icon); + fourLinesIcon.setTextLine2("Line 2"); + fourLinesIcon.setTextLine3("Line 3"); + fourLinesIcon.setTextLine4("Line 4"); + + hi.add(oneLineIconEmblem). + add(twoLinesNoIcon). + add(twoLinesIconEmblem). + add(twoLinesIconEmblemHorizontal). + add(twoLinesIconCheckBox). + add(fourLinesIcon); + hi.show(); + // end::the-components-of-codename-one-java-158[] + return hi; + } +} diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava160Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/SpanLabelFigure.java similarity index 52% rename from docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava160Snippet.java rename to docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/SpanLabelFigure.java index 87a89451fdd..f0e801d487a 100644 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava160Snippet.java +++ b/docs/demos/common/src/main/java/com/codenameone/developerguide/screenshots/SpanLabelFigure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * 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 @@ -20,76 +20,30 @@ * Please contact Codename One through http://www.codenameone.com/ if you * need additional information or have any questions. */ -package com.codenameone.developerguide.snippets.generated; -import com.codename1.gpu.*; -import com.codename1.ui.*; -import com.codename1.ui.animations.*; -import com.codename1.ui.events.*; -import com.codename1.ui.geom.*; -import com.codename1.ui.layouts.*; -import com.codename1.ui.list.*; -import com.codename1.ui.plaf.*; -import com.codename1.ui.util.*; -import com.codename1.components.*; -import com.codename1.charts.models.*; -import com.codename1.charts.renderers.*; -import com.codename1.charts.views.*; -import com.codename1.capture.*; -import com.codename1.io.*; -import com.codename1.l10n.*; -import com.codename1.location.*; -import com.codename1.maps.*; -import com.codename1.media.*; -import com.codename1.messaging.*; -import com.codename1.payment.*; -import com.codename1.processing.*; -import com.codename1.properties.*; -import com.codename1.push.*; -import com.codename1.security.*; -import com.codename1.social.*; -import com.codename1.ui.spinner.*; -import java.io.*; -import com.codename1.components.ToastBar.Status; -import com.codename1.maps.layers.*; -import com.codename1.charts.*; -import com.codename1.ui.validation.*; -import com.codename1.xml.*; -import com.codename1.charts.util.*; -import com.codename1.javascript.*; -import com.codename1.ui.tree.*; -import com.codename1.ui.table.*; -import com.codename1.contacts.*; -import java.util.*; +package com.codenameone.developerguide.screenshots; +import com.codename1.components.SpanLabel; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Image; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; -class TheComponentsOfCodenameOneJava160Snippet { - +/// The SpanLabel figure for the Components chapter. +/// +/// The tagged region below is what the chapter includes, so the listing beside +/// the picture is the code that drew it. That is the point of moving these out +/// of the compile-only snippet fixtures: a fixture and a screenshot are two +/// sources for one example and drift apart silently. +public final class SpanLabelFigure implements GuideFigure { + @Override + public String id() { + return "components-spanlabel"; + } - Object context; - Object url; - Object value; - Object body; - Object event; - String apiKey = "test-key"; - String myHttpsURL = "https://example.com"; - java.util.List validKeysList = new java.util.ArrayList<>(); - Image myImage; - Graphics graphics; - Graphics g; - GraphicsDevice device; - Form form; - Form hi; - Container cnt; - Container myForm; - Component component; - Button button; - MultiButton myMultiButton; - Label label; - BrowserComponent browserComponent; - Resources theme; - - void snippet() throws Exception { + @Override + public Form build() { // tag::the-components-of-codename-one-java-160[] Form hi = new Form("SpanLabel", new BoxLayout(BoxLayout.Y_AXIS)); Image icon = FontImage.createMaterial(FontImage.MATERIAL_INFO, "Label", 3.0f); @@ -107,7 +61,6 @@ void snippet() throws Exception { hi.add(d).add(l).add(r).add(c); hi.show(); // end::the-components-of-codename-one-java-160[] + return hi; } - - } diff --git a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava069Snippet.java b/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava069Snippet.java deleted file mode 100644 index 652161ec3b9..00000000000 --- a/docs/demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava069Snippet.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2012, 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.codenameone.developerguide.snippets.generated; - -import com.codename1.ui.*; -import com.codename1.ui.layouts.*; -import com.codename1.ui.table.*; - -class TheComponentsOfCodenameOneJava069Snippet { - void snippet() { - // tag::the-components-of-codename-one-java-069[] - Form hi = new Form("Table", new BorderLayout()); - TableModel model = new DefaultTableModel( - new String[] {"Col 1", "Col 2", "Col 3"}, - new Object[][] { - {"Row 1", "Row A", "Row X"}, - {"Row 2", "Row B can now stretch", null}, - {"Row 3", "Row C", "Row Z"}, - {"Row 4", "Row D", "Row K"}, - }) { - public boolean isCellEditable(int row, int col) { - return col != 0; - } - }; - Table table = new Table(model) { - @Override - protected TableLayout.Constraint createCellConstraint(Object value, int row, int column) { - TableLayout.Constraint con = super.createCellConstraint(value, row, column); - if (row == 1 && column == 1) { - con.setHorizontalSpan(2); - } - con.setWidthPercentage(33); - return con; - } - }; - hi.add(BorderLayout.CENTER, table); - hi.show(); - // end::the-components-of-codename-one-java-069[] - } -} diff --git a/docs/demos/javase/pom.xml b/docs/demos/javase/pom.xml index a4ea8bb9bb4..470c6bf8e08 100644 --- a/docs/demos/javase/pom.xml +++ b/docs/demos/javase/pom.xml @@ -284,12 +284,57 @@ + generate-guide-screenshots verify exec + + + -classpath + + ${exec.mainClass} + ${guide.screenshot.output} + schematic + + + + + generate-guide-figures-ios + verify + + exec + + + + -classpath + + ${exec.mainClass} + ${guide.screenshot.output} + ios + + + + + generate-guide-figures-android + verify + + exec + + + + -classpath + + ${exec.mainClass} + ${guide.screenshot.output} + android + + diff --git a/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotDesktopStub.java b/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotDesktopStub.java index 4dab0d0324a..288ce8b2f76 100644 --- a/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotDesktopStub.java +++ b/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotDesktopStub.java @@ -1,10 +1,36 @@ +/* + * 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.codenameone.developerguide; import com.codename1.ui.Display; +import com.codenameone.developerguide.screenshots.FigureDevice; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.io.File; +import java.util.Locale; +import java.util.TimeZone; import javax.swing.JFrame; import javax.swing.SwingUtilities; @@ -13,6 +39,7 @@ * JavaSE desktop entry point used by docs automation to regenerate guide screenshots. */ public final class GuideScreenshotDesktopStub implements Runnable { + private static final String MODE_SCHEMATIC = "schematic"; private static final String APP_TITLE = "Guide Screenshots"; private static final String APP_NAME = "DemoCodeScreenshots"; private static final String APP_VERSION = "1.0"; @@ -28,6 +55,46 @@ public static void main(String[] args) { System.setProperty("java.awt.headless", "false"); System.setProperty("guide.screenshot.output", args.length > 0 ? args[0] : "target/generated-guide-screenshots"); + // "schematic" draws the layout diagrams; a device key ("ios", "android") + // draws the themed figures at that profile. They are separate JVMs because + // the density below is read once during Display.init and then cached on the + // implementation instance, so one process cannot render two profiles. + String mode = args.length > 1 ? args[1] : MODE_SCHEMATIC; + System.setProperty("guide.screenshot.mode", mode); + + // Pin what the host would otherwise decide. retinaScale is computed from + // the display and multiplies the system font sizes above 1.5, so a Retina + // Mac and a CI runner disagree without it. + System.setProperty("cn1.retinaScale", "1"); + + // The same argument applies to the locale and the time zone, and they are + // easier to miss because most figures do not visibly depend on them. + // JavaSE's L10NManager is built from Locale.getDefault(), so currency, + // number, date, language and country strings all follow whoever ran the + // generator -- the committed localization figure was rendered on a Mac set + // to Israel and could not be reproduced anywhere else. Set them before + // Display.init, because the implementation reads them as it comes up. + Locale.setDefault(Locale.US); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + + // A simulator skin decides platform name, fonts, geometry and device class, + // and it is selected from these two properties: JavaSEPort.hasSkins() is + // "skin != null || dskin != null", and every skin path sits behind it, the + // stored preference included. Whoever launches the generator could have + // either set -- from a shell, an IDE run configuration, an inherited + // environment -- and the figures would then be rendered against that + // device's state while still being written to the same golden files. The + // renderer refuses an iOS skin by platform name, but an Android skin + // reports "and" and would pass that check while changing everything. + // Clearing both is what actually guarantees no skin loads. + System.clearProperty("skin"); + System.clearProperty("dskin"); + if (!MODE_SCHEMATIC.equals(mode)) { + FigureDevice device = FigureDevice.fromKey(mode); + System.setProperty("cn1.javase.pixelMilliRatio", + String.valueOf(device.pixelMilliRatio())); + } + frame = new JFrame(APP_TITLE); Display.init(frame.getContentPane()); Display.getInstance().setProperty("AppName", APP_NAME); @@ -57,7 +124,9 @@ public void run() { @Override public void run() { try { - GuideScreenshotGeneratorStub.generateInto(new File(System.getProperty("guide.screenshot.output"))); + GuideScreenshotGeneratorStub.generateInto( + new File(System.getProperty("guide.screenshot.output")), + System.getProperty("guide.screenshot.mode", MODE_SCHEMATIC)); } catch (Exception err) { err.printStackTrace(); System.exit(1); diff --git a/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotGeneratorStub.java b/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotGeneratorStub.java index cf04e238dae..005e9d4dba9 100644 --- a/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotGeneratorStub.java +++ b/docs/demos/javase/src/main/java/com/codenameone/developerguide/GuideScreenshotGeneratorStub.java @@ -1,5 +1,31 @@ +/* + * 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.codenameone.developerguide; +import com.codenameone.developerguide.screenshots.FigureDevice; +import com.codenameone.developerguide.screenshots.GuideFigureRenderer; +import com.codenameone.developerguide.screenshots.GuideFigures; import com.codenameone.developerguide.screenshots.PreAdvancedThemingScreenshots; import java.io.File; @@ -15,20 +41,33 @@ private GuideScreenshotGeneratorStub() { } public static void main(String[] args) throws Exception { - generateInto(new File(args.length > 0 ? args[0] : "target/generated-guide-screenshots")); + generateInto(new File(args.length > 0 ? args[0] : "target/generated-guide-screenshots"), + args.length > 1 ? args[1] : "schematic"); } - public static void generateInto(File outputDirectory) throws IOException { - System.out.println("Generating guide screenshots into: " + outputDirectory.getAbsolutePath()); + public static void generateInto(File outputDirectory, String mode) throws IOException { + System.out.println("Generating guide screenshots (" + mode + ") into: " + + outputDirectory.getAbsolutePath()); if (!outputDirectory.exists() && !outputDirectory.mkdirs()) { throw new IOException("Unable to create screenshot output directory: " + outputDirectory); } - PreAdvancedThemingScreenshots.generate(new PreAdvancedThemingScreenshots.ScreenshotSink() { + final File target = outputDirectory; + if ("schematic".equals(mode)) { + PreAdvancedThemingScreenshots.generate(new PreAdvancedThemingScreenshots.ScreenshotSink() { + @Override + public OutputStream open(String fileName) throws IOException { + System.out.println("Generating guide screenshot: " + fileName); + return new FileOutputStream(new File(target, fileName)); + } + }); + return; + } + GuideFigureRenderer.render(new GuideFigureRenderer.ScreenshotSink() { @Override public OutputStream open(String fileName) throws IOException { - System.out.println("Generating guide screenshot: " + fileName); - return new FileOutputStream(new File(outputDirectory, fileName)); + System.out.println("Generating guide figure: " + fileName); + return new FileOutputStream(new File(target, fileName)); } - }); + }, FigureDevice.fromKey(mode), GuideFigures.variants()); } } diff --git a/docs/developer-guide/Miscellaneous-Features.asciidoc b/docs/developer-guide/Miscellaneous-Features.asciidoc index 6c0db2a75b0..36713449967 100644 --- a/docs/developer-guide/Miscellaneous-Features.asciidoc +++ b/docs/developer-guide/Miscellaneous-Features.asciidoc @@ -242,11 +242,11 @@ The resource bundle is a map between keys and values for example: the code below [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/MiscellaneousFeaturesJava016Snippet.java[tag=miscellaneous-features-java-016,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/L10nBasicFigure.java[tag=miscellaneous-features-java-016,indent=0] ---- .Localized label -image::img/l10n-basic.png[Localized label,scaledwidth=30%] +image::img/generated/l10n-basic.png[Localized label,scaledwidth=30%] ==== Localization manager diff --git a/docs/developer-guide/Native-Themes.asciidoc b/docs/developer-guide/Native-Themes.asciidoc index 7bed149c932..cb5bfb64c15 100644 --- a/docs/developer-guide/Native-Themes.asciidoc +++ b/docs/developer-guide/Native-Themes.asciidoc @@ -61,6 +61,13 @@ compiler rewrites each rule inside that block into a `$Dark` style entry; UIManager picks them up whenever `CN.isDarkMode() == true`. +.One form under Android Material, rendered in each appearance +image:img/generated/native-themes-appearance-light.png[Light appearance,scaledwidth=45%] +image:img/generated/native-themes-appearance-dark.png[Dark appearance,scaledwidth=45%] + +Nothing in that form is styled by hand, so the difference between the two +pictures is entirely what the theme's dark entries supply. + `Display.getInstance().setDarkMode(Boolean)` controls the active appearance: diff --git a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc index e88b7f9120b..691c4fc4632 100644 --- a/docs/developer-guide/The-Components-Of-Codename-One.asciidoc +++ b/docs/developer-guide/The-Components-Of-Codename-One.asciidoc @@ -340,11 +340,11 @@ https://www.codenameone.com/javadoc/com/codename1/ui/Label.html[Label] represent [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava146Snippet.java[tag=the-components-of-codename-one-java-146,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLabelTextPositionFigure.java[tag=the-components-of-codename-one-java-146,indent=0] ---- .Label positions -image::img/components-label-text-position.png[Label positions,scaledwidth=20%] +image::img/generated/components-label-text-position.png[Label positions,scaledwidth=20%] `Label` allows a single line of text, line breaking is an expensive operation on mobile devices footnote:[String width is the real expensive part here, the complexity of font kerning and the recursion required to reflow text is a big performance hurdle] and so the `Label` class doesn't support it. @@ -622,18 +622,22 @@ Here is a trivial hello world style `Button`: include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava030Snippet.java[tag=the-components-of-codename-one-java-030,indent=0] ---- -.Simple button in the iOS styling, notice iOS doesn't have borders on buttons... -image::img/components-button.png[Simple button in the iOS styling, notice iOS doesn't have borders on buttons...,scaledwidth=40%] +The same three buttons under the two modern native themes. A button takes its +shape, fill and font from the theme, so the platform difference costs no code: + +.The same buttons under iOS Modern (left) and Android Material (right) +image:img/generated/components-button-ios.png[iOS Modern,scaledwidth=45%] +image:img/generated/components-button-android.png[Android Material,scaledwidth=45%] Such a button can be styled to look like a link using code like this or by making these settings in the theme and using code such as `btn.setUIID("Hyperlink")`: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava031Snippet.java[tag=the-components-of-codename-one-java-031,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsLinkButtonFigure.java[tag=the-components-of-codename-one-java-031,indent=0] ---- .Button styled to look like a link -image::img/components-link-button.png[Button styled to look like a link,scaledwidth=40%] +image::img/generated/components-link-button.png[Button styled to look like a link,scaledwidth=40%] ==== Uppercase buttons @@ -696,11 +700,11 @@ Notice in the sample below that you associate all the radio buttons with a group [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava155Snippet.java[tag=the-components-of-codename-one-java-155,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/CheckBoxFigure.java[tag=the-components-of-codename-one-java-155,indent=0] ---- .RadioButton & CheckBox usage -image::img/components-radiobutton-checkbox.png[RadioButton & CheckBox usage,scaledwidth=20%] +image::img/generated/components-radiobutton-checkbox.png[RadioButton & CheckBox usage,scaledwidth=20%] Both of these components can be displayed as toggle buttons (see the toggle button section below), or use the default check mark/filled circle appearance based on the type/OS. @@ -783,11 +787,11 @@ A common source of confusion in the `MultiButton` is the difference between the [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava158Snippet.java[tag=the-components-of-codename-one-java-158,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/MultiButtonFigure.java[tag=the-components-of-codename-one-java-158,indent=0] ---- .Multiple usage scenarios for the MultiButton -image::img/components-multibutton.png[Multiple usage scenarios for the MultiButton,scaledwidth=20%] +image::img/generated/components-multibutton.png[Multiple usage scenarios for the MultiButton,scaledwidth=20%] ==== Styling the MultiButton @@ -808,11 +812,11 @@ Unlike the `MultiButton` it uses the `TextArea` internally to break lines seamle [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava159Snippet.java[tag=the-components-of-codename-one-java-159,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsSpanbuttonFigure.java[tag=the-components-of-codename-one-java-159,indent=0] ---- .The SpanButton Component -image::img/components-spanbutton.png[The SpanButton Component,scaledwidth=20%] +image::img/generated/components-spanbutton.png[The SpanButton Component,scaledwidth=20%] TIP: `SpanButton` is slower than both `Button` and `MultiButton`. Recommend using it when there is a genuine need for its functionality. @@ -827,11 +831,11 @@ One of the features of label that moved into `SpanLabel` to some extent is the a [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava160Snippet.java[tag=the-components-of-codename-one-java-160,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/SpanLabelFigure.java[tag=the-components-of-codename-one-java-160,indent=0] ---- .The SpanLabel Component -image::img/components-spanlabel.png[The SpanLabel Component,scaledwidth=20%] +image::img/generated/components-spanlabel.png[The SpanLabel Component,scaledwidth=20%] TIP: `SpanLabel` is slower than `Label`. Recommend using it when there is a genuine need for its functionality. @@ -1348,11 +1352,11 @@ Here is a trivial sample of using the standard table component: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava068Snippet.java[tag=the-components-of-codename-one-java-068,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableFigure.java[tag=the-components-of-codename-one-java-068,indent=0] ---- .Simple Table usage -image::img/components-table.png[Simple Table usage,scaledwidth=20%] +image::img/generated/components-table.png[Simple Table usage,scaledwidth=20%] NOTE: In the sample above the title area and first column aren't editable. The other two columns are editable. @@ -1362,11 +1366,11 @@ For example: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava069Snippet.java[tag=the-components-of-codename-one-java-069,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTableWithSpanningFigure.java[tag=the-components-of-codename-one-java-069,indent=0] ---- .Table with spanning & fixed widths to 33% -image::img/components-table-with-spanning.png[Table with spanning and fixed widths to 33%,scaledwidth=20%] +image::img/generated/components-table-with-spanning.png[Table with spanning and fixed widths to 33%,scaledwidth=20%] To customize the table cell behavior you can derive the `Table` to create a "renderer like" widget, but unlike the list this component is "kept" and used as is. This means you can bind listeners to this component and work with it as you would with any other component in Codename One. @@ -1374,11 +1378,11 @@ The example above can be extended to include far more capabilities: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava070Snippet.java[tag=the-components-of-codename-one-java-070,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsTablePinstripeFigure.java[tag=the-components-of-codename-one-java-070,indent=0] ---- .Table with customize cells using the pinstripe effect -image::img/components-table-pinstripe.png[Table with customize cells using the pinstripe effect,scaledwidth=20%] +image::img/generated/components-table-pinstripe.png[Table with customize cells using the pinstripe effect,scaledwidth=20%] .Picker table cell during edit image::img/components-table-pinstripe-edit.png[Picker table cell during edit,scaledwidth=20%] @@ -1757,11 +1761,11 @@ The code below provides a brief overview of these options: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava191Snippet.java[tag=the-components-of-codename-one-java-191,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/ComponentsToolbarFigure.java[tag=the-components-of-codename-one-java-191,indent=0] ---- .The Toolbar -image::img/components-toolbar.png[The Toolbar,scaledwidth=25%] +image::img/generated/components-toolbar.png[The Toolbar,scaledwidth=25%] .The default sidemenu of the Toolbar @@ -2636,13 +2640,13 @@ The code below adds a simple badge to a chat button: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/TheComponentsOfCodenameOneJava140Snippet.java[tag=the-components-of-codename-one-java-140,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/BadgeFloatingButtonFigure.java[tag=the-components-of-codename-one-java-140,indent=0] ---- The code above results in this, notice you can type into the text field to change the badge value: .Badge floating button in action -image::img/badge-floating-button.png[Badge floating button in action,scaledwidth=20%] +image::img/generated/badge-floating-button.png[Badge floating button in action,scaledwidth=20%] === SplitPane diff --git a/docs/developer-guide/basics.asciidoc b/docs/developer-guide/basics.asciidoc index 88d97cb21c9..fa7bed30cef 100644 --- a/docs/developer-guide/basics.asciidoc +++ b/docs/developer-guide/basics.asciidoc @@ -211,7 +211,7 @@ To sum this up, you can use layout managers and nesting to create elaborate UI's [[flow-layout]] .Flow Layout -image::img/flow-layout.png[Flow Layout,scaledwidth=20%] +image::img/generated/flow-layout.png[Flow Layout,scaledwidth=20%] link:https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.html[Flow layout] lets the components "flow" horizontally and break a line when reaching the edge of the container. It's the default layout manager for containers. Because it's so flexible it's also problematic as it can result in wrong preferred size values for the parent https://www.codenameone.com/javadoc/com/codename1/ui/Container.html[Container]. This can create a reflow issue, as a result recommend using flow layout for trivial cases. Avoid it for things such as text input etc. As the size of the text input can vary in runtime: @@ -231,15 +231,15 @@ Flow layout can be aligned to the left (the default), to the <> [[box-layout-y]] .BoxLayout Y -image::img/box-layout-y.png[BoxLayout Y,scaledwidth=20%] +image::img/generated/box-layout-y.png[BoxLayout Y,scaledwidth=20%] Box layout also supports a shorter terse notation which you use here to <>: @@ -271,7 +271,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g [[box-layout-x]] .BoxLayout X -image::img/box-layout-x.png[BoxLayout X,scaledwidth=20%] +image::img/generated/box-layout-x.png[BoxLayout X,scaledwidth=20%] The box layout keeps the preferred size of its destination orientation and scales elements on the other axis. Specifically `X_AXIS` will keep the preferred width of the component while growing all the components vertically to match in size. Its `Y_AXIS` counterpart keeps the preferred height while growing the components horizontally. @@ -281,7 +281,7 @@ This behavior is useful since it allows elements to align as they would all have Sometimes the growing behavior in the X axis is undesired, for these cases you can use the `X_AXIS_NO_GROW` variant. .BoxLayout X_AXIS_NO_GROW -image::img/box-layout-x-no-grow.png[BoxLayout X_AXIS_NO_GROW,scaledwidth=20%] +image::img/generated/box-layout-x-no-grow.png[BoxLayout X_AXIS_NO_GROW,scaledwidth=20%] .`FlowLayout` vs. `BoxLayout.X_AXIS` TIP: When applicable recommend `BoxLayout` over `FlowLayout` as it acts more consistently in all situations. Another advantage of `BoxLayout` is the fact that it grows and thus aligns the components in a consistent column or row @@ -289,7 +289,7 @@ TIP: When applicable recommend `BoxLayout` over `FlowLayout` as it acts more con ===== Border Layout .Border Layout -image::img/border-layout.png[Border Layout,scaledwidth=20%] +image::img/generated/border-layout.png[Border Layout,scaledwidth=20%] link:https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html[Border layout] is unique. `BorderLayout` is a constraint-based layout that can place up to five components in one of the five positions: `NORTH`, `SOUTH`, `EAST`, `WEST` or `CENTER`: @@ -314,7 +314,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g Results in: .Border Layout with CENTER_BEHAVIOR_CENTER -image::img/border-layout-center.png[Border Layout with CENTER_BEHAVIOR_CENTER,scaledwidth=20%] +image::img/generated/border-layout-center.png[Border Layout with CENTER_BEHAVIOR_CENTER,scaledwidth=20%] .Scrolling is Disabled in Border Layout NOTE: Because of its scaling behavior scrolling a border @@ -323,7 +323,7 @@ layout makes no sense. `Container` implicitly blocks scrolling on a border layou For RTL the EAST and WEST values are implicitly reversed as shown in this image: .Border Layout in RTL mode -image::img/border-layout-RTL.png[Border Layout in RTL mode,scaledwidth=20%] +image::img/generated/border-layout-RTL.png[Border Layout in RTL mode,scaledwidth=20%] .RTL and Bidi **** @@ -353,12 +353,12 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .Grid Layout 2×2 -image::img/grid-layout-2x2.png[Grid Layout 2×2,scaledwidth=20%] +image::img/generated/grid-layout-2x2.png[Grid Layout 2×2,scaledwidth=20%] When you use a 2×4 size ratio you would see elements getting cropped as you do here. The grid layout uses the grid size first and doesn't pay too much attention to the preferred size of the components it holds. .Grid Layout 2×4 -image::img/grid-layout-2x4.png[Grid Layout 2×4,scaledwidth=20%] +image::img/generated/grid-layout-2x4.png[Grid Layout 2×4,scaledwidth=20%] Grid also has an autoFit attribute that can be used to automatically calculate the column count based on available space and preferred width. This is useful for working with UI's where the device orientation @@ -376,10 +376,10 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .Grid Layout autofit portrait -image::img/grid-layout-autofit-portrait.png[Grid Layout autofit portrait,scaledwidth=20%] +image::img/generated/grid-layout-autofit-portrait.png[Grid Layout autofit portrait,scaledwidth=20%] .Grid Layout autofit landscape -image::img/grid-layout-autofit-landscape.png[Grid Layout autofit landscape,scaledwidth=20%] +image::img/generated/grid-layout-autofit-landscape.png[Grid Layout autofit landscape,scaledwidth=20%] [[table-layout-section]] ==== Table Layout @@ -400,7 +400,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .2×2 TableLayout with 5 elements, notice that the last element is missing -image::img/table-layout-2x2.png[2×2 TableLayout with 5 elements, notice that the last element is missing,scaledwidth=20%] +image::img/generated/table-layout-2x2.png[2×2 TableLayout with 5 elements, notice that the last element is missing,scaledwidth=20%] `TableLayout` supports the ability to grow the last column which can be enabled using the `setGrowHorizontally` method. You can also use a shortened terse syntax to construct a `TableLayout` but since the `TableLayout` is a constraint based layout you won't be able to use its full power with this syntax. @@ -412,7 +412,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .`TableLayout.encloseIn()` with default behavior of growing the last column -image::img/table-layout-enclose.png[TableLayout.encloseIn() with default behavior of growing the last column,scaledwidth=20%] +image::img/generated/table-layout-enclose.png[TableLayout.encloseIn() with default behavior of growing the last column,scaledwidth=20%] ===== The full potential @@ -461,7 +461,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g [[table-layout-constraints]] .TableLayout constraints can be used to create elaborate UI's -image::img/table-layout-constraints.png[TableLayout constraints can be used to create elaborate UI's,scaledwidth=20%] +image::img/generated/table-layout-constraints.png[TableLayout constraints can be used to create elaborate UI's,scaledwidth=20%] ===== TextMode Layout @@ -497,7 +497,7 @@ When working in the non-android environment you use a `BoxLayout` on the Y axis When used without constraints, the https://www.codenameone.com/javadoc/com/codename1/ui/layouts/LayeredLayout.html[LayeredLayout] places the components in order one on top of the other and sizes them all to the size of the largest component. This is useful when trying to create an overlay on top of an existing component. For example: an "x" button to allow removing the component. .The X on this button was placed there using the layered layout code below -image::img/layered-layout.png[The X on this button was placed there using the layered layout code below,scaledwidth=20%] +image::img/generated/layered-layout.png[The X on this button was placed there using the layered layout code below,scaledwidth=20%] The code to generate this UI is slightly complex and contains few relevant pieces. The truly relevant piece is this block: @@ -541,7 +541,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g The result is: -image::img/guibuilder-2-insets-1.png[Button positioned in bottom right using insets,scaledwidth=20%] +image::img/generated/guibuilder-2-insets-1.png[Button positioned in bottom right using insets,scaledwidth=20%] The thing new here is this line: @@ -573,7 +573,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g Resulting in: .Button vertically centered 5mm from left edge -image::img/guibuilder-2-insets-2.png[Button vertically centered 5mm from left edge,scaledwidth=20%] +image::img/generated/guibuilder-2-insets-2.png[Button vertically centered 5mm from left edge,scaledwidth=20%] Move it to the right edge with: @@ -616,7 +616,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g This would result in: .Button's left inset linked to text field -image::img/guibuilder-2-insets-3.png[Button's left inset linked to text field,scaledwidth=50%] +image::img/generated/guibuilder-2-insets-3.png[Button's left inset linked to text field,scaledwidth=50%] The two active lines here are the last two: @@ -675,7 +675,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g Because of the way GridBag works there's no terse syntax for it, although one should be possible. .GridbagLayout sample from the Java tutorial running on Codename One -image::img/gridbag-layout.png[GridbagLayout sample from the Java tutorial running on Codename One,scaledwidth=20%] +image::img/generated/gridbag-layout.png[GridbagLayout sample from the Java tutorial running on Codename One,scaledwidth=20%] ==== Group Layout @@ -693,7 +693,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .GroupLayout Matisse generated UI running in Codename One -image::img/group-layout.png[GroupLayout Matisse generated UI running in Codename One,scaledwidth=20%] +image::img/generated/group-layout.png[GroupLayout Matisse generated UI running in Codename One,scaledwidth=20%] If you're porting newer Matisse code there are simple changes you can do: @@ -716,7 +716,7 @@ include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/g ---- .MiG layout sample ported to Codename One -image::img/mig-layout.png[MiG layout sample ported to Codename One,scaledwidth=20%] +image::img/generated/mig-layout.png[MiG layout sample ported to Codename One,scaledwidth=20%] It should be reasonably easy to port MiG code but you should notice the following: diff --git a/docs/developer-guide/graphics.asciidoc b/docs/developer-guide/graphics.asciidoc index 87a67dfd834..d38466afd76 100644 --- a/docs/developer-guide/graphics.asciidoc +++ b/docs/developer-guide/graphics.asciidoc @@ -34,11 +34,11 @@ A paint method can be implemented as such: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava001Snippet.java[tag=graphics-java-001,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsHiworldFigure.java[tag=graphics-java-001,indent=0] ---- -.Hi world demo code, notice that the blue bar on top is the iOS7+ status bar -image::img/graphics-hiworld.png[Hi world demo code, notice that the blue bar on top is the iOS7+ status bar,scaledwidth=20%] +.Hi world demo code under the iOS theme; the paint fills the form and the status bar strip above it belongs to the platform +image::img/generated/graphics-hiworld.png[Hi world demo code under the iOS theme,scaledwidth=20%] ==== Painting with gradients @@ -137,11 +137,11 @@ many user interfaces [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava006Snippet.java[tag=graphics-java-006,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsGlasspaneFigure.java[tag=graphics-java-006,indent=0] ---- .The glass pane draws the warning sign on the border of the component peeking out -image::img/graphics-glasspane.png[The glass pane draws the warning sign on the border of the component peeking out,scaledwidth=20%] +image::img/generated/graphics-glasspane.png[The glass pane draws the warning sign on the border of the component peeking out,scaledwidth=20%] === Shapes & transforms @@ -799,11 +799,11 @@ Icon fonts can be created in 2 basic ways the first is explicitly by defining al [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava032Snippet.java[tag=graphics-java-032,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageFixedFigure.java[tag=graphics-java-032,indent=0] ---- .Icon font from material design icons created with the fixed size of display width -image::img/graphics-fontimage-fixed.png[Icon font from material design icons created with the fixed size of display width,scaledwidth=20%] +image::img/generated/graphics-fontimage-fixed.png[Icon font from material design icons created with the fixed size of display width,scaledwidth=20%] NOTE: The samples use the built-in material design icon font. This is for convenience so the sample will work out of the box, for everyone. For example you should be able to do this with any arbitrary icon font off the internet as long as its a valid TTF file. @@ -813,11 +813,11 @@ There are two versions of this method: the first one expects the `Style` object [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava033Snippet.java[tag=graphics-java-033,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageStyleFigure.java[tag=graphics-java-033,indent=0] ---- .An image created from the Style object -image::img/graphics-fontimage-style.png[An image created from the Style object,scaledwidth=20%] +image::img/generated/graphics-fontimage-style.png[An image created from the Style object,scaledwidth=20%] WARNING: Notice that for this specific version of the method the size of the font is used to determine the icon size. In the other methods for `FontImage` creation the size of the font is ignored! @@ -833,11 +833,11 @@ To recreate the save icon from above you can do something like: [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/GraphicsJava034Snippet.java[tag=graphics-java-034,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/GraphicsFontimageMaterialFigure.java[tag=graphics-java-034,indent=0] ---- .Material save icon -image::img/graphics-fontimage-material.png[Material save icon,scaledwidth=20%] +image::img/generated/graphics-fontimage-material.png[Material save icon,scaledwidth=20%] NOTE: Notice that the icon is smaller now as it's calculated based on the font size of the `Button` UIID. diff --git a/docs/developer-guide/img/badge-floating-button.png b/docs/developer-guide/img/badge-floating-button.png deleted file mode 100644 index 0f58843e95f..00000000000 Binary files a/docs/developer-guide/img/badge-floating-button.png and /dev/null differ diff --git a/docs/developer-guide/img/components-button.png b/docs/developer-guide/img/components-button.png deleted file mode 100644 index b82862a5b52..00000000000 Binary files a/docs/developer-guide/img/components-button.png and /dev/null differ diff --git a/docs/developer-guide/img/components-label-text-position.png b/docs/developer-guide/img/components-label-text-position.png deleted file mode 100644 index 3e87e5906fa..00000000000 Binary files a/docs/developer-guide/img/components-label-text-position.png and /dev/null differ diff --git a/docs/developer-guide/img/components-link-button.png b/docs/developer-guide/img/components-link-button.png deleted file mode 100644 index 2e240bfdd3c..00000000000 Binary files a/docs/developer-guide/img/components-link-button.png and /dev/null differ diff --git a/docs/developer-guide/img/components-multibutton.png b/docs/developer-guide/img/components-multibutton.png deleted file mode 100644 index a3a152adb57..00000000000 Binary files a/docs/developer-guide/img/components-multibutton.png and /dev/null differ diff --git a/docs/developer-guide/img/components-radiobutton-checkbox.png b/docs/developer-guide/img/components-radiobutton-checkbox.png deleted file mode 100644 index 20d869d6a42..00000000000 Binary files a/docs/developer-guide/img/components-radiobutton-checkbox.png and /dev/null differ diff --git a/docs/developer-guide/img/components-spanbutton.png b/docs/developer-guide/img/components-spanbutton.png deleted file mode 100644 index 360acf19803..00000000000 Binary files a/docs/developer-guide/img/components-spanbutton.png and /dev/null differ diff --git a/docs/developer-guide/img/components-spanlabel.png b/docs/developer-guide/img/components-spanlabel.png deleted file mode 100644 index e515cd00490..00000000000 Binary files a/docs/developer-guide/img/components-spanlabel.png and /dev/null differ diff --git a/docs/developer-guide/img/components-table-pinstripe.png b/docs/developer-guide/img/components-table-pinstripe.png deleted file mode 100644 index 3a5d6bd33ac..00000000000 Binary files a/docs/developer-guide/img/components-table-pinstripe.png and /dev/null differ diff --git a/docs/developer-guide/img/components-table-with-spanning.png b/docs/developer-guide/img/components-table-with-spanning.png deleted file mode 100644 index 62c86f84bd1..00000000000 Binary files a/docs/developer-guide/img/components-table-with-spanning.png and /dev/null differ diff --git a/docs/developer-guide/img/components-table.png b/docs/developer-guide/img/components-table.png deleted file mode 100644 index a49ba4578eb..00000000000 Binary files a/docs/developer-guide/img/components-table.png and /dev/null differ diff --git a/docs/developer-guide/img/components-toolbar.png b/docs/developer-guide/img/components-toolbar.png deleted file mode 100644 index e950de05ea2..00000000000 Binary files a/docs/developer-guide/img/components-toolbar.png and /dev/null differ diff --git a/docs/developer-guide/img/csv-parsing.png b/docs/developer-guide/img/csv-parsing.png deleted file mode 100644 index 85ff40366dd..00000000000 Binary files a/docs/developer-guide/img/csv-parsing.png and /dev/null differ diff --git a/docs/developer-guide/img/generated/badge-floating-button.png b/docs/developer-guide/img/generated/badge-floating-button.png new file mode 100644 index 00000000000..9b7f86a4a59 Binary files /dev/null and b/docs/developer-guide/img/generated/badge-floating-button.png differ diff --git a/docs/developer-guide/img/generated/badge-floating-button.tolerance b/docs/developer-guide/img/generated/badge-floating-button.tolerance new file mode 100644 index 00000000000..eadd298bcf9 --- /dev/null +++ b/docs/developer-guide/img/generated/badge-floating-button.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.0902% of pixels differ, worst +# channel delta 60. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 84 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=84 +maxMismatchPercent=0.13 diff --git a/docs/developer-guide/img/border-layout-RTL.png b/docs/developer-guide/img/generated/border-layout-RTL.png similarity index 100% rename from docs/developer-guide/img/border-layout-RTL.png rename to docs/developer-guide/img/generated/border-layout-RTL.png diff --git a/docs/developer-guide/img/border-layout-center.png b/docs/developer-guide/img/generated/border-layout-center.png similarity index 100% rename from docs/developer-guide/img/border-layout-center.png rename to docs/developer-guide/img/generated/border-layout-center.png diff --git a/docs/developer-guide/img/border-layout.png b/docs/developer-guide/img/generated/border-layout.png similarity index 100% rename from docs/developer-guide/img/border-layout.png rename to docs/developer-guide/img/generated/border-layout.png diff --git a/docs/developer-guide/img/box-layout-x-no-grow.png b/docs/developer-guide/img/generated/box-layout-x-no-grow.png similarity index 100% rename from docs/developer-guide/img/box-layout-x-no-grow.png rename to docs/developer-guide/img/generated/box-layout-x-no-grow.png diff --git a/docs/developer-guide/img/box-layout-x.png b/docs/developer-guide/img/generated/box-layout-x.png similarity index 100% rename from docs/developer-guide/img/box-layout-x.png rename to docs/developer-guide/img/generated/box-layout-x.png diff --git a/docs/developer-guide/img/box-layout-y.png b/docs/developer-guide/img/generated/box-layout-y.png similarity index 100% rename from docs/developer-guide/img/box-layout-y.png rename to docs/developer-guide/img/generated/box-layout-y.png diff --git a/docs/developer-guide/img/generated/components-button-android.png b/docs/developer-guide/img/generated/components-button-android.png new file mode 100644 index 00000000000..28689c06088 Binary files /dev/null and b/docs/developer-guide/img/generated/components-button-android.png differ diff --git a/docs/developer-guide/img/generated/components-button-ios.png b/docs/developer-guide/img/generated/components-button-ios.png new file mode 100644 index 00000000000..d94b7193a24 Binary files /dev/null and b/docs/developer-guide/img/generated/components-button-ios.png differ diff --git a/docs/developer-guide/img/generated/components-label-text-position.png b/docs/developer-guide/img/generated/components-label-text-position.png new file mode 100644 index 00000000000..2594ec6829e Binary files /dev/null and b/docs/developer-guide/img/generated/components-label-text-position.png differ diff --git a/docs/developer-guide/img/generated/components-label-text-position.tolerance b/docs/developer-guide/img/generated/components-label-text-position.tolerance new file mode 100644 index 00000000000..dfa2cb15af5 --- /dev/null +++ b/docs/developer-guide/img/generated/components-label-text-position.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.4521% of pixels differ, worst +# channel delta 134. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 158 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=158 +maxMismatchPercent=0.63 diff --git a/docs/developer-guide/img/generated/components-link-button.png b/docs/developer-guide/img/generated/components-link-button.png new file mode 100644 index 00000000000..fb2f3403470 Binary files /dev/null and b/docs/developer-guide/img/generated/components-link-button.png differ diff --git a/docs/developer-guide/img/generated/components-multibutton.png b/docs/developer-guide/img/generated/components-multibutton.png new file mode 100644 index 00000000000..fd017432870 Binary files /dev/null and b/docs/developer-guide/img/generated/components-multibutton.png differ diff --git a/docs/developer-guide/img/generated/components-multibutton.tolerance b/docs/developer-guide/img/generated/components-multibutton.tolerance new file mode 100644 index 00000000000..8a468391a11 --- /dev/null +++ b/docs/developer-guide/img/generated/components-multibutton.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.2210% of pixels differ, worst +# channel delta 134. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 158 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=158 +maxMismatchPercent=0.31 diff --git a/docs/developer-guide/img/generated/components-radiobutton-checkbox.png b/docs/developer-guide/img/generated/components-radiobutton-checkbox.png new file mode 100644 index 00000000000..84970bf41fd Binary files /dev/null and b/docs/developer-guide/img/generated/components-radiobutton-checkbox.png differ diff --git a/docs/developer-guide/img/generated/components-radiobutton-checkbox.tolerance b/docs/developer-guide/img/generated/components-radiobutton-checkbox.tolerance new file mode 100644 index 00000000000..89f56b6a6c8 --- /dev/null +++ b/docs/developer-guide/img/generated/components-radiobutton-checkbox.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.5440% of pixels differ, worst +# channel delta 134. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 158 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=158 +maxMismatchPercent=0.76 diff --git a/docs/developer-guide/img/generated/components-spanbutton.png b/docs/developer-guide/img/generated/components-spanbutton.png new file mode 100644 index 00000000000..3efe836fdf3 Binary files /dev/null and b/docs/developer-guide/img/generated/components-spanbutton.png differ diff --git a/docs/developer-guide/img/generated/components-spanbutton.tolerance b/docs/developer-guide/img/generated/components-spanbutton.tolerance new file mode 100644 index 00000000000..4bd671ca6f8 --- /dev/null +++ b/docs/developer-guide/img/generated/components-spanbutton.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.1108% of pixels differ, worst +# channel delta 136. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 160 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=160 +maxMismatchPercent=0.16 diff --git a/docs/developer-guide/img/generated/components-spanlabel.png b/docs/developer-guide/img/generated/components-spanlabel.png new file mode 100644 index 00000000000..a7d61a145ff Binary files /dev/null and b/docs/developer-guide/img/generated/components-spanlabel.png differ diff --git a/docs/developer-guide/img/generated/components-spanlabel.tolerance b/docs/developer-guide/img/generated/components-spanlabel.tolerance new file mode 100644 index 00000000000..0478c162bdb --- /dev/null +++ b/docs/developer-guide/img/generated/components-spanlabel.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.2847% of pixels differ, worst +# channel delta 136. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 160 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=160 +maxMismatchPercent=0.4 diff --git a/docs/developer-guide/img/generated/components-table-pinstripe.png b/docs/developer-guide/img/generated/components-table-pinstripe.png new file mode 100644 index 00000000000..ba62524947e Binary files /dev/null and b/docs/developer-guide/img/generated/components-table-pinstripe.png differ diff --git a/docs/developer-guide/img/generated/components-table-with-spanning.png b/docs/developer-guide/img/generated/components-table-with-spanning.png new file mode 100644 index 00000000000..3a9b0d36b37 Binary files /dev/null and b/docs/developer-guide/img/generated/components-table-with-spanning.png differ diff --git a/docs/developer-guide/img/generated/components-table.png b/docs/developer-guide/img/generated/components-table.png new file mode 100644 index 00000000000..04c63c40d47 Binary files /dev/null and b/docs/developer-guide/img/generated/components-table.png differ diff --git a/docs/developer-guide/img/generated/components-toolbar.png b/docs/developer-guide/img/generated/components-toolbar.png new file mode 100644 index 00000000000..9c8100aa995 Binary files /dev/null and b/docs/developer-guide/img/generated/components-toolbar.png differ diff --git a/docs/developer-guide/img/generated/components-toolbar.tolerance b/docs/developer-guide/img/generated/components-toolbar.tolerance new file mode 100644 index 00000000000..d249cd4275d --- /dev/null +++ b/docs/developer-guide/img/generated/components-toolbar.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.8954% of pixels differ, worst +# channel delta 99. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 123 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=123 +maxMismatchPercent=1.25 diff --git a/docs/developer-guide/img/generated/csv-parsing.png b/docs/developer-guide/img/generated/csv-parsing.png new file mode 100644 index 00000000000..302e4c55049 Binary files /dev/null and b/docs/developer-guide/img/generated/csv-parsing.png differ diff --git a/docs/developer-guide/img/flow-layout-center-middle.png b/docs/developer-guide/img/generated/flow-layout-center-middle.png similarity index 100% rename from docs/developer-guide/img/flow-layout-center-middle.png rename to docs/developer-guide/img/generated/flow-layout-center-middle.png diff --git a/docs/developer-guide/img/flow-layout-center.png b/docs/developer-guide/img/generated/flow-layout-center.png similarity index 100% rename from docs/developer-guide/img/flow-layout-center.png rename to docs/developer-guide/img/generated/flow-layout-center.png diff --git a/docs/developer-guide/img/flow-layout-right.png b/docs/developer-guide/img/generated/flow-layout-right.png similarity index 100% rename from docs/developer-guide/img/flow-layout-right.png rename to docs/developer-guide/img/generated/flow-layout-right.png diff --git a/docs/developer-guide/img/flow-layout.png b/docs/developer-guide/img/generated/flow-layout.png similarity index 100% rename from docs/developer-guide/img/flow-layout.png rename to docs/developer-guide/img/generated/flow-layout.png diff --git a/docs/developer-guide/img/generated/graphics-fontimage-fixed.png b/docs/developer-guide/img/generated/graphics-fontimage-fixed.png new file mode 100644 index 00000000000..321f41e238f Binary files /dev/null and b/docs/developer-guide/img/generated/graphics-fontimage-fixed.png differ diff --git a/docs/developer-guide/img/generated/graphics-fontimage-material.png b/docs/developer-guide/img/generated/graphics-fontimage-material.png new file mode 100644 index 00000000000..827d61fae4f Binary files /dev/null and b/docs/developer-guide/img/generated/graphics-fontimage-material.png differ diff --git a/docs/developer-guide/img/generated/graphics-fontimage-material.tolerance b/docs/developer-guide/img/generated/graphics-fontimage-material.tolerance new file mode 100644 index 00000000000..10c1a429eab --- /dev/null +++ b/docs/developer-guide/img/generated/graphics-fontimage-material.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.2498% of pixels differ, worst +# channel delta 30. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 54 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=54 +maxMismatchPercent=0.35 diff --git a/docs/developer-guide/img/generated/graphics-fontimage-style.png b/docs/developer-guide/img/generated/graphics-fontimage-style.png new file mode 100644 index 00000000000..27a59366b89 Binary files /dev/null and b/docs/developer-guide/img/generated/graphics-fontimage-style.png differ diff --git a/docs/developer-guide/img/generated/graphics-fontimage-style.tolerance b/docs/developer-guide/img/generated/graphics-fontimage-style.tolerance new file mode 100644 index 00000000000..6a42620ee73 --- /dev/null +++ b/docs/developer-guide/img/generated/graphics-fontimage-style.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.4322% of pixels differ, worst +# channel delta 56. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 80 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=80 +maxMismatchPercent=0.61 diff --git a/docs/developer-guide/img/generated/graphics-glasspane.png b/docs/developer-guide/img/generated/graphics-glasspane.png new file mode 100644 index 00000000000..5ada8c3267c Binary files /dev/null and b/docs/developer-guide/img/generated/graphics-glasspane.png differ diff --git a/docs/developer-guide/img/generated/graphics-glasspane.tolerance b/docs/developer-guide/img/generated/graphics-glasspane.tolerance new file mode 100644 index 00000000000..d4127a65f3e --- /dev/null +++ b/docs/developer-guide/img/generated/graphics-glasspane.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.1304% of pixels differ, worst +# channel delta 64. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 88 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=88 +maxMismatchPercent=0.18 diff --git a/docs/developer-guide/img/generated/graphics-hiworld.png b/docs/developer-guide/img/generated/graphics-hiworld.png new file mode 100644 index 00000000000..99379310a71 Binary files /dev/null and b/docs/developer-guide/img/generated/graphics-hiworld.png differ diff --git a/docs/developer-guide/img/generated/graphics-hiworld.tolerance b/docs/developer-guide/img/generated/graphics-hiworld.tolerance new file mode 100644 index 00000000000..c472ffb0b80 --- /dev/null +++ b/docs/developer-guide/img/generated/graphics-hiworld.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.0937% of pixels differ, worst +# channel delta 255. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 255 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=255 +maxMismatchPercent=0.13 diff --git a/docs/developer-guide/img/grid-layout-2x2.png b/docs/developer-guide/img/generated/grid-layout-2x2.png similarity index 100% rename from docs/developer-guide/img/grid-layout-2x2.png rename to docs/developer-guide/img/generated/grid-layout-2x2.png diff --git a/docs/developer-guide/img/grid-layout-2x4.png b/docs/developer-guide/img/generated/grid-layout-2x4.png similarity index 100% rename from docs/developer-guide/img/grid-layout-2x4.png rename to docs/developer-guide/img/generated/grid-layout-2x4.png diff --git a/docs/developer-guide/img/grid-layout-autofit-landscape.png b/docs/developer-guide/img/generated/grid-layout-autofit-landscape.png similarity index 100% rename from docs/developer-guide/img/grid-layout-autofit-landscape.png rename to docs/developer-guide/img/generated/grid-layout-autofit-landscape.png diff --git a/docs/developer-guide/img/grid-layout-autofit-portrait.png b/docs/developer-guide/img/generated/grid-layout-autofit-portrait.png similarity index 100% rename from docs/developer-guide/img/grid-layout-autofit-portrait.png rename to docs/developer-guide/img/generated/grid-layout-autofit-portrait.png diff --git a/docs/developer-guide/img/gridbag-layout.png b/docs/developer-guide/img/generated/gridbag-layout.png similarity index 100% rename from docs/developer-guide/img/gridbag-layout.png rename to docs/developer-guide/img/generated/gridbag-layout.png diff --git a/docs/developer-guide/img/group-layout.png b/docs/developer-guide/img/generated/group-layout.png similarity index 100% rename from docs/developer-guide/img/group-layout.png rename to docs/developer-guide/img/generated/group-layout.png diff --git a/docs/developer-guide/img/guibuilder-2-insets-1.png b/docs/developer-guide/img/generated/guibuilder-2-insets-1.png similarity index 100% rename from docs/developer-guide/img/guibuilder-2-insets-1.png rename to docs/developer-guide/img/generated/guibuilder-2-insets-1.png diff --git a/docs/developer-guide/img/guibuilder-2-insets-2.png b/docs/developer-guide/img/generated/guibuilder-2-insets-2.png similarity index 100% rename from docs/developer-guide/img/guibuilder-2-insets-2.png rename to docs/developer-guide/img/generated/guibuilder-2-insets-2.png diff --git a/docs/developer-guide/img/guibuilder-2-insets-3.png b/docs/developer-guide/img/generated/guibuilder-2-insets-3.png similarity index 100% rename from docs/developer-guide/img/guibuilder-2-insets-3.png rename to docs/developer-guide/img/generated/guibuilder-2-insets-3.png diff --git a/docs/developer-guide/img/generated/l10n-basic.png b/docs/developer-guide/img/generated/l10n-basic.png new file mode 100644 index 00000000000..12f9032b2cd Binary files /dev/null and b/docs/developer-guide/img/generated/l10n-basic.png differ diff --git a/docs/developer-guide/img/generated/layered-layout.png b/docs/developer-guide/img/generated/layered-layout.png new file mode 100644 index 00000000000..2f0ffc6966c Binary files /dev/null and b/docs/developer-guide/img/generated/layered-layout.png differ diff --git a/docs/developer-guide/img/generated/layered-layout.tolerance b/docs/developer-guide/img/generated/layered-layout.tolerance new file mode 100644 index 00000000000..10102a7cc66 --- /dev/null +++ b/docs/developer-guide/img/generated/layered-layout.tolerance @@ -0,0 +1,14 @@ +# Glyph rasterization differs between macOS and Linux for the same bundled font +# at the same size, so this figure does not come back byte-identical when it is +# regenerated on a developer's machine. Measured against the CI runner's own +# output, which is what the committed image is: 0.1126% of pixels differ, worst +# channel delta 141. +# +# Both bounds are set from that measurement rather than left wide open. +# maxMismatchPercent limits how much of the image may change AT ALL -- every +# differing pixel counts, not only those past the delta -- so a wholesale recolour +# cannot creep through in small steps. maxChannelDelta then caps how far any one +# pixel may move; 165 clears the measured worst with room while a solid overwrite +# still fails it. A blanket 255 would have disabled this bound entirely. +maxChannelDelta=165 +maxMismatchPercent=0.16 diff --git a/docs/developer-guide/img/mig-layout.png b/docs/developer-guide/img/generated/mig-layout.png similarity index 100% rename from docs/developer-guide/img/mig-layout.png rename to docs/developer-guide/img/generated/mig-layout.png diff --git a/docs/developer-guide/img/generated/native-themes-appearance-dark.png b/docs/developer-guide/img/generated/native-themes-appearance-dark.png new file mode 100644 index 00000000000..ffa24f34f45 Binary files /dev/null and b/docs/developer-guide/img/generated/native-themes-appearance-dark.png differ diff --git a/docs/developer-guide/img/generated/native-themes-appearance-light.png b/docs/developer-guide/img/generated/native-themes-appearance-light.png new file mode 100644 index 00000000000..92749c50b8a Binary files /dev/null and b/docs/developer-guide/img/generated/native-themes-appearance-light.png differ diff --git a/docs/developer-guide/img/table-layout-2x2.png b/docs/developer-guide/img/generated/table-layout-2x2.png similarity index 100% rename from docs/developer-guide/img/table-layout-2x2.png rename to docs/developer-guide/img/generated/table-layout-2x2.png diff --git a/docs/developer-guide/img/table-layout-constraints.png b/docs/developer-guide/img/generated/table-layout-constraints.png similarity index 100% rename from docs/developer-guide/img/table-layout-constraints.png rename to docs/developer-guide/img/generated/table-layout-constraints.png diff --git a/docs/developer-guide/img/table-layout-enclose.png b/docs/developer-guide/img/generated/table-layout-enclose.png similarity index 100% rename from docs/developer-guide/img/table-layout-enclose.png rename to docs/developer-guide/img/generated/table-layout-enclose.png diff --git a/docs/developer-guide/img/graphics-fontimage-fixed.png b/docs/developer-guide/img/graphics-fontimage-fixed.png deleted file mode 100644 index 0489c4197d4..00000000000 Binary files a/docs/developer-guide/img/graphics-fontimage-fixed.png and /dev/null differ diff --git a/docs/developer-guide/img/graphics-fontimage-material.png b/docs/developer-guide/img/graphics-fontimage-material.png deleted file mode 100644 index 1bf0d8ee623..00000000000 Binary files a/docs/developer-guide/img/graphics-fontimage-material.png and /dev/null differ diff --git a/docs/developer-guide/img/graphics-fontimage-style.png b/docs/developer-guide/img/graphics-fontimage-style.png deleted file mode 100644 index 8138a4ad203..00000000000 Binary files a/docs/developer-guide/img/graphics-fontimage-style.png and /dev/null differ diff --git a/docs/developer-guide/img/graphics-glasspane.png b/docs/developer-guide/img/graphics-glasspane.png deleted file mode 100644 index cc4446b326e..00000000000 Binary files a/docs/developer-guide/img/graphics-glasspane.png and /dev/null differ diff --git a/docs/developer-guide/img/graphics-hiworld.png b/docs/developer-guide/img/graphics-hiworld.png deleted file mode 100644 index e6dc202656d..00000000000 Binary files a/docs/developer-guide/img/graphics-hiworld.png and /dev/null differ diff --git a/docs/developer-guide/img/l10n-basic.png b/docs/developer-guide/img/l10n-basic.png deleted file mode 100644 index ac0822b1777..00000000000 Binary files a/docs/developer-guide/img/l10n-basic.png and /dev/null differ diff --git a/docs/developer-guide/img/layered-layout.png b/docs/developer-guide/img/layered-layout.png deleted file mode 100644 index 2c8aa052ecd..00000000000 Binary files a/docs/developer-guide/img/layered-layout.png and /dev/null differ diff --git a/docs/developer-guide/img/layered-layout.tolerance b/docs/developer-guide/img/layered-layout.tolerance deleted file mode 100644 index 4f1e24ed098..00000000000 --- a/docs/developer-guide/img/layered-layout.tolerance +++ /dev/null @@ -1,15 +0,0 @@ -# This figure contains a FontImage material glyph. Measured against the CI -# runner's own output, the glyph lands at exactly the same size and position -- -# a 55x49 bounding box at the same origin -- and differs only in antialiased -# edge coverage: 946 fully-white pixels against 916, 173 differing pixels in -# total, 0.113% of the image. Java2D rasterizes the same glyph from the same -# bundled font at the same size slightly differently on macOS and Linux. -# -# The two bounds are independent and both bind. maxMismatchPercent limits how -# much of the image may change AT ALL -- every differing pixel counts, not only -# those past the delta -- so a wholesale recolour cannot slip through by moving -# each channel a little at a time. maxChannelDelta then caps how far any single -# pixel may move: 160 clears the measured worst of 141 with room, while a solid -# overwrite still fails on it. -maxChannelDelta=160 -maxMismatchPercent=0.3 diff --git a/docs/developer-guide/io.asciidoc b/docs/developer-guide/io.asciidoc index 94ef33766ac..28d81b3c9d4 100644 --- a/docs/developer-guide/io.asciidoc +++ b/docs/developer-guide/io.asciidoc @@ -778,11 +778,11 @@ To parse a CSV use the https://www.codenameone.com/javadoc/com/codename1/io/CSVP [source,java] ---- -include::../demos/common/src/main/java/com/codenameone/developerguide/snippets/generated/IoJava113Snippet.java[tag=io-java-113,indent=0] +include::../demos/common/src/main/java/com/codenameone/developerguide/screenshots/CsvParsingFigure.java[tag=io-java-113,indent=0] ---- .CSV parsing results, notice the escaped parentheses and comma -image::img/csv-parsing.png[CSV parsing results notice the escaped parentheses and comma,scaledwidth=30%] +image::img/generated/csv-parsing.png[CSV parsing results notice the escaped parentheses and comma,scaledwidth=30%] The data contains a two-dimensional array of the CSV content. You can change the delimiter character by using the `CSVParser` constructor that accepts a character. diff --git a/scripts/developer-guide/check-guide-links.py b/scripts/developer-guide/check-guide-links.py index 9782e320550..88bc8ab1ea6 100755 --- a/scripts/developer-guide/check-guide-links.py +++ b/scripts/developer-guide/check-guide-links.py @@ -1049,9 +1049,33 @@ def main() -> int: ) return 1 + # Java sources embed absolute URLs to guide figures -- com.codename1.ui's + # package documentation carries 37 of them -- and those URLs are derived from + # the file layout, because the site build rsyncs docs/developer-guide/ to + # /developer-guide/. Moving or renaming a figure therefore breaks them + # silently: nothing else here reads a Java file, and the javadoc renders a + # missing image without complaint. It has drifted three times, so it is + # checked rather than remembered. + figure_url = re.compile( + r"codenameone\.com/developer-guide/img/([A-Za-z0-9/._-]+\.(?:png|jpg|gif))" + ) + guide_img = Path("docs/developer-guide/img") + dangling = [] + for source in sorted(Path("CodenameOne/src").rglob("*.java")): + text = source.read_text(encoding="utf-8", errors="replace") + for relative in figure_url.findall(text): + if not (guide_img / relative).is_file(): + dangling.append(f"{source}: img/{relative}") + if dangling: + for entry in dangling: + print(f"::error::figure URL points at a file that does not ship: {entry}", file=sys.stderr) + print(f"\n{len(dangling)} dangling figure URL(s) in Java sources.", file=sys.stderr) + return 1 + print( f"Links: {sum(current.values())} known bad link(s) against {len(known)} known site paths " - f"and {len(patterns)} redirect rule(s); none new, none stale." + f"and {len(patterns)} redirect rule(s); none new, none stale. " + f"Every figure URL embedded in a Java source resolves." ) return 0 diff --git a/scripts/developer-guide/compare-screenshots.py b/scripts/developer-guide/compare-screenshots.py index a57efcaca40..d8c3471c1f4 100755 --- a/scripts/developer-guide/compare-screenshots.py +++ b/scripts/developer-guide/compare-screenshots.py @@ -67,6 +67,17 @@ def compare(generated: Path, committed: Path, tolerance: tuple[int, float] | Non a = Image.open(generated).convert("RGBA") b = Image.open(committed).convert("RGBA") if a.size != b.size: + # A tolerance sidecar deliberately does NOT cover this: a different size is + # a different picture, not a rendering difference. + # + # Note the figure heights are cropped to the laid-out content, and text + # component heights come from font metrics, which Java2D rounds slightly + # differently on macOS and Linux -- a handful of figures land two to five + # pixels apart between a developer's machine and the runner. The committed + # images are the runner's output, so CI compares byte-for-byte; a local + # regeneration of those figures reports a size change here and that is + # expected rather than a regression. Do not commit locally regenerated + # images to silence it -- that only moves the failure to CI. return f"size changed: generated {a.size[0]}x{a.size[1]}, committed {b.size[0]}x{b.size[1]}" pa, pb = a.load(), b.load() width, height = a.size @@ -98,15 +109,47 @@ def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--generated", required=True, type=Path) parser.add_argument("--committed", required=True, type=Path) - parser.add_argument("--expected-count", type=int, required=True) args = parser.parse_args() produced = sorted(args.generated.glob("*.png")) - if len(produced) != args.expected_count: - print( - f"::error::Expected {args.expected_count} generated figures, found {len(produced)}", - file=sys.stderr, - ) + committed_figures = sorted(args.committed.glob("*.png")) + if not produced: + print("::error::The generator produced no figures at all", file=sys.stderr) + return 1 + + # The committed directory is the manifest. Checking only that each produced + # figure matches would let a golden quietly stop being generated -- the run + # would compare whatever was still produced and pass, and the stale image + # would keep shipping. Requiring the two sets to be equal makes a figure + # that fell out of the generator a failure instead of a silent gap. + missing = sorted({c.name for c in committed_figures} - {p.name for p in produced}) + if missing: + for name in missing: + print( + f"::error::{name} is committed as a generated figure but nothing produced it", + file=sys.stderr, + ) + return 1 + + # Two generated figures with identical bytes mean one of them is showing + # something its caption does not describe. It happens whenever a sample only + # SETS UP an interaction -- attaches a dialog to a button, defines a callback, + # names a menu -- and the harness photographs the launch form instead of the + # state the caption promises. Nothing else catches it: each such figure + # renders successfully, is not blank, and matches its own committed copy, so + # both the render and the comparison pass while the guide shows the reader + # the same picture twice under two different captions. + by_bytes: dict[bytes, list[str]] = {} + for image in produced: + by_bytes.setdefault(image.read_bytes(), []).append(image.name) + clashes = [names for names in by_bytes.values() if len(names) > 1] + if clashes: + for names in clashes: + print( + "::error::these figures are byte-identical, so at least one does not " + "show what its caption says: " + ", ".join(sorted(names)), + file=sys.stderr, + ) return 1 failures = 0