Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
45e2e50
Add the Maven-first GUI Builder rewrite
shai-almog Aug 5, 2026
94c80e7
Fix table cell shuffling and the stale canvas after a drop
shai-almog Aug 5, 2026
c8cedfe
Hold the whole tree to account after every nested move
shai-almog Aug 5, 2026
0389de0
Add demo forms covering the nesting cases that keep breaking
shai-almog Aug 5, 2026
e57f84c
Stop an emptied container from pushing its neighbour off the canvas
shai-almog Aug 5, 2026
91984b6
Revert demo theme colours left over from a manual CSS test
shai-almog Aug 5, 2026
f51ae1e
Say why a keystroke was refused instead of ignoring it
shai-almog Aug 5, 2026
8c21fd4
Leave the caret alone when an edit is refused
shai-almog Aug 5, 2026
735c45d
Type real keystrokes at the editor in an integration test
shai-almog Aug 5, 2026
3e86576
Stop the drag guide overlay from swallowing every click on the editor
shai-almog Aug 6, 2026
d6a13f7
Make the Java editor typable and stop a stale record eating the P key
shai-almog Aug 6, 2026
fb7954a
Stop the Edit menu stealing keys from the code editor
shai-almog Aug 6, 2026
c5beab6
Repaint the canvas when CSS restyles it, and focus the CSS editor onc…
shai-almog Aug 6, 2026
7d410b5
Keep an open editor alive when the canvas is rebuilt
shai-almog Aug 6, 2026
16af33a
Deliver every keystroke, give the canvas its own theme, verify what s…
shai-almog Aug 6, 2026
608476e
Let a text component have the letter p back
shai-almog Aug 6, 2026
cb334c9
Drive a CSS edit from the keyboard in a test, and repaint the form
shai-almog Aug 6, 2026
08b58f4
Verify the canvas is styled by the project's own stylesheet
shai-almog Aug 6, 2026
273d0fe
Report resolved styles over MCP, and see the canvas ignoring the styl…
shai-almog Aug 6, 2026
39fe857
Correct the record on canvas styling
shai-almog Aug 6, 2026
77a40cd
Add the required copyright headers to every new file
shai-almog Aug 6, 2026
36a36d7
Copy built artifacts into the local repository instead of trusting in…
shai-almog Aug 6, 2026
d935c90
Address the PR review: generated source, save safety and IDE bindings
shai-almog Aug 6, 2026
a57891c
Give the GUI form scaffolder the project's copyright header
shai-almog Aug 6, 2026
dac2aee
Document the GUI builder that ships, with screenshots of a new project
shai-almog Aug 6, 2026
9432968
Give a pasted table child a free cell
shai-almog Aug 6, 2026
d50773d
Match the guide's American spelling in the GUI builder section
shai-almog Aug 7, 2026
8d6feaf
Report a failed replacement instead of a successful save
shai-almog Aug 7, 2026
78e2f14
Leave multi key mode alone when the application already owns it
shai-almog Aug 7, 2026
4907e49
Build the GUI Builder for Java 8 so every project can open it
shai-almog Aug 7, 2026
eec5b34
Restore the Java 8 build and fix a batch of review findings
shai-almog Aug 7, 2026
1b185f2
Close the gaps the last batch of fixes opened
shai-almog Aug 7, 2026
d9f7fd2
Generate the binding model on save and respect table spans
shai-almog Aug 7, 2026
60cc255
Refuse layouts and cells the runtime cannot honour
shai-almog Aug 7, 2026
7cc49fd
Route desktop shell actions correctly and keep the model in step
shai-almog Aug 8, 2026
b535922
Apply the guards to every path that reaches the same behaviour
shai-almog Aug 8, 2026
10db885
Finish the threading split and stop misrepresenting Container roots
shai-almog Aug 8, 2026
07f6839
Write the handler stub the generated listener needs, and fit pasted s…
shai-almog Aug 8, 2026
59c0132
Reopen only the pane that was regenerated, and bound spans
shai-almog Aug 8, 2026
5e0fc86
Keep model saves off the companion path and store the palette's text
shai-almog Aug 8, 2026
fd05916
Hand Backspace to the editor and stop offering controls that do nothing
shai-almog Aug 8, 2026
b143079
Close the paths the earlier guards missed
shai-almog Aug 8, 2026
907ac85
Fix the four defects the last push introduced
shai-almog Aug 8, 2026
38e3c4e
Keep the buffer when reopening the pane you are editing
shai-almog Aug 8, 2026
ea337d6
Check occupancy, not just bounds, and match declarations properly
shai-almog Aug 8, 2026
9ccb5a0
Validate the displaced component too, and match declarations by shape
shai-almog Aug 9, 2026
3a2ef9b
Stop the paste search spinning, and order the save so failure is safe
shai-almog Aug 9, 2026
fa7db3f
Bind editor state to its document, and keep the old file until the re…
shai-almog Aug 9, 2026
bd8939a
Stage the save so a failure leaves the project untouched
shai-almog Aug 9, 2026
8413b4c
Apply the rules symmetrically: redo, drop ordering, rollback deletion
shai-almog Aug 9, 2026
ffd7b5b
Point the second call site at the same rule rather than a copy of it
shai-almog Aug 9, 2026
f9f41f0
Mirror CSS as it is typed, and put the saved theme back on discard
shai-almog Aug 9, 2026
730613a
Defer the first model too, and stop explicit opens nesting the canvas
shai-almog Aug 9, 2026
1757f8c
Do not record a carried buffer as the saved text
shai-almog Aug 9, 2026
9a58358
Match declarations by shape, claim accessor names, refresh the model …
shai-almog Aug 9, 2026
fc77335
Abandon a rejected drop instead of undoing it with more edits
shai-almog Aug 9, 2026
331efae
Reserve span rectangles, validate swaps, parse the handler signature
shai-almog Aug 9, 2026
37b3a09
Handle legacy migration cases and drop onto the pointed-at container
shai-almog Aug 9, 2026
092ecb8
Preview a component with the UIID it will actually run with
shai-almog Aug 9, 2026
db04ec0
Live preview: SpanLabel gap, RadioButton selection, and their markers
shai-almog Aug 9, 2026
96d135e
One source of truth for defaults, and a marker that must own its line
shai-almog Aug 9, 2026
47c1b4c
Apply markerLine to its sibling, balance parens, keep disabled disabled
shai-almog Aug 9, 2026
83e9980
Save the form with the source it generated, and guard both ends of a …
shai-almog Aug 9, 2026
d864ea1
Route every marker search through markerLine, and create a missing to…
shai-almog Aug 9, 2026
dd325f1
Sweep markers across the branch, fix root UIID and handler collisions
shai-almog Aug 9, 2026
e910983
Draw the Form and its content pane as the two layers the runtime has
shai-almog Aug 9, 2026
dd4d52e
Build legacy aliases from the original names, keep literals out of th…
shai-almog Aug 10, 2026
3c462f7
Regenerate the model from either save, detach on table drops, allow t…
shai-almog Aug 10, 2026
f67a12d
Keep the developer's constructors and supertypes through legacy migra…
shai-almog Aug 10, 2026
cae90d9
Fail the save when migration is refused, keep custom Resources overloads
shai-almog Aug 10, 2026
02f4b1e
Route root UIID edits to the Form layer, rewrite aliases as tokens only
shai-almog Aug 10, 2026
4d7e5f1
Backspace edits focused text, Cmd+S saves the focused pane, honest UIIDs
shai-almog Aug 10, 2026
f91d44b
Protect the whole marker line, fix the caret, consume qualified annot…
shai-almog Aug 10, 2026
9d061b7
Refuse rather than discard: damaged markers, customised scaffold cons…
shai-almog Aug 10, 2026
fa0b386
Mask comments when finding constructor ends, delete whole code points
shai-almog Aug 10, 2026
6daf8ab
Read-only editors release soft keys, migration keeps class annotations
shai-almog Aug 10, 2026
84de20a
Refuse generic legacy classes, reject pasting a screen into a screen
shai-almog Aug 10, 2026
25dec5c
Share the GUI Builder launch guard across the reactor
shai-almog Aug 10, 2026
b22d763
Name the generated root, put its own properties on the Form layer
shai-almog Aug 10, 2026
2ffab07
Find legacy markers and the class keyword as tokens, not substrings
shai-almog Aug 10, 2026
6a0fc87
Strip reserved characters from names, refuse abstract legacy classes
shai-almog Aug 10, 2026
5697b1c
Bound the carried region by the form's brace, read past annotations
shai-almog Aug 10, 2026
f55b7ec
Keep trailing helper classes, imports with comments, annotated constr…
shai-almog Aug 10, 2026
0b7e7a9
Keep a trailing helper on every save, not only through migration
shai-almog Aug 10, 2026
68bbc9c
Custom super calls are user logic, and the form is found by name
shai-almog Aug 10, 2026
aee8682
Reattach the classDeclaration javadoc the name lookup was inserted be…
shai-almog Aug 10, 2026
4e46cb6
Only the scaffold's own chain counts as scaffolding
shai-almog Aug 10, 2026
95f90b3
Do not recommit inline text the model already holds
shai-almog Aug 10, 2026
205986a
Balance the delegate's parameter scan, refuse annotated parameters
shai-almog Aug 10, 2026
ab36cce
Exact initializer call, honest constraint picker, move among components
shai-almog Aug 10, 2026
d12bcaf
Stop the guibuilder job hanging, keep annotations and legal accessors
shai-almog Aug 10, 2026
9cc75b9
Compare superclasses whole, accept only the scaffold's initializer ar…
shai-almog Aug 10, 2026
a5f7c56
Carry the interfaces on every save, and stop deleting same-named methods
shai-almog Aug 10, 2026
8414ff2
Handle a blank command handler, an unreadable inset, and form scrolling
shai-almog Aug 10, 2026
6d9165c
Read the scrolling default from one place
shai-almog Aug 10, 2026
d1db35d
Resolve an unqualified superclass against the file's imports
shai-almog Aug 10, 2026
86ab0b1
Scope the handler check to the form, and see through marker annotations
shai-almog Aug 10, 2026
4f8d9e9
Save the text the editor shows, and regenerate a model the form outgrew
shai-almog Aug 10, 2026
314c409
Compare bound names, and the initializer argument against the real pa…
shai-almog Aug 10, 2026
dc070d1
Ask before replacing a model, and count only real binding declarations
shai-almog Aug 10, 2026
5b3358b
Escape carriage returns, and find a JDK off macOS
shai-almog Aug 10, 2026
7d53216
Make "nobody can answer a dialog" a condition rather than a per-site …
shai-almog Aug 10, 2026
ab1a64a
Keep the delegate's throws clause, and let a same-package class shadow
shai-almog Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .github/workflows/guibuilder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: GUI Builder

# The standalone GUI Builder lives outside the Maven reactor (scripts/guibuilder)
# and PR CI ignores scripts/**, so nothing else compiles it. It depends on core
# APIs (CodeEditor protected regions, LayeredLayout baseline units) that can be
# changed in the same PR, which is exactly how the editor silently stops
# compiling against a freshly built core.

on:
workflow_dispatch:
pull_request:
branches:
- master
paths:
- '.github/workflows/guibuilder.yml'
- 'scripts/guibuilder/**'
- '!scripts/guibuilder/**/*.md'
# The editor is an ordinary Codename One app: it renders the design canvas with
# Tabs, TableLayout, SplitPane and the rest of the core UI, compiles the project
# stylesheet with the CSS compiler, and runs on the JavaSE port. Listing only the
# few classes it obviously touches let a core change break it with this job skipped.
- 'CodenameOne/src/**'
- 'Ports/JavaSE/src/**'
- 'maven/css-compiler/**'
- 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/**'
push:
branches:
- master
paths:
- '.github/workflows/guibuilder.yml'
- 'scripts/guibuilder/**'
- '!scripts/guibuilder/**/*.md'
# The editor is an ordinary Codename One app: it renders the design canvas with
# Tabs, TableLayout, SplitPane and the rest of the core UI, compiles the project
# stylesheet with the CSS compiler, and runs on the JavaSE port. Listing only the
# few classes it obviously touches let a core change break it with this job skipped.
- 'CodenameOne/src/**'
- 'Ports/JavaSE/src/**'
- 'maven/css-compiler/**'
- 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/**'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
guibuilder:
runs-on: ubuntu-latest
timeout-minutes: 60
container: ghcr.io/codenameone/codenameone/pr-ci-container:latest
defaults:
run:
shell: bash

steps:
- uses: actions/checkout@v6

- name: Cache Maven dependencies
uses: actions/cache@v5
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2

- name: Prepare Codename One binaries
# The JavaSE port needs jfxrt.jar from cn1-binaries; without it the
# build fails with a large, misleading set of missing javafx.* errors.
run: |
set -euo pipefail
rm -rf maven/target/cn1-binaries
mkdir -p maven/target
cp -r /opt/cn1-binaries maven/target/cn1-binaries

- name: Install Codename One artifacts (JDK 8)
# JAVA_HOME_8 / JAVA_HOME_17 are container image environment variables,
# so they are expanded by the shell rather than the env context.
run: |
set -euo pipefail
export JAVA_HOME="${JAVA_HOME_8}"
export PATH="${JAVA_HOME}/bin:${PATH}"
cd maven
mvn -B install -Plocal-dev-javase -DskipTests \
-Darchetype.test.skip=true -Dmaven.javadoc.skip=true \
-Dcn1.binaries="${GITHUB_WORKSPACE}/maven/target/cn1-binaries"

# Built and tested on JDK 8 so that the editor stays loadable on every JDK a Codename One
# project may be built with. A newer JDK here would let a post-8 API through unnoticed.
- name: Run standalone GUI Builder tests (JDK 8)
run: |
set -euo pipefail
export JAVA_HOME="${JAVA_HOME_8}"
export PATH="${JAVA_HOME}/bin:${PATH}"
cd scripts/guibuilder
xvfb-run -a mvn -B -pl javase -am clean test -Dcodename1.platform=javase

- name: Package the executable GUI Builder (JDK 8)
run: |
set -euo pipefail
export JAVA_HOME="${JAVA_HOME_8}"
export PATH="${JAVA_HOME}/bin:${PATH}"
cd scripts/guibuilder
xvfb-run -a mvn -B -pl javase -am -Pexecutable-jar package \
-Dcodename1.platform=javase -Dmaven.test.skip=true
ls javase/target/codenameone-guibuilder-*.jar

- name: Upload GUI Builder test reports
if: always()
uses: actions/upload-artifact@v7
with:
name: guibuilder-test-reports
path: scripts/guibuilder/javase/target/surefire-reports
if-no-files-found: warn
retention-days: 14
100 changes: 92 additions & 8 deletions .github/workflows/release-on-maven-central.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
# The four Central confirmation polls each cover the documented 30-minute
# The five Central confirmation polls each cover the documented 30-minute
# propagation window, and each fires only when its own deploy reported failure,
# so the worst case is ~2h of polling on top of the build. Bounded explicitly
# so the worst case is ~2.5h of polling on top of the build. Bounded explicitly
# rather than inheriting the 6h default, so a wedged release fails in a shift
# rather than overnight.
timeout-minutes: 240
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
export GPG_TTY=$(tty)
# Once dual publish is off, R2 is the repository and Central must not receive
# new immutable releases. skipPublishing still stages, so the R2 upload that
# follows is unaffected -- same treatment as the three editor deploys.
# follows is unaffected -- same treatment as the four editor deploys.
skip_publishing=true
if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ]; then
skip_publishing=false
Expand Down Expand Up @@ -246,7 +246,7 @@ jobs:
continue-on-error: true
# always(): central-publishing stages before it uploads, so the tree exists
# even when the Central deploy above failed -- which is precisely the case
# R2 is here to survive. Metadata is regenerated once, after all three.
# R2 is here to survive. Metadata is regenerated once, after all four.
if: always() && steps.deploy_gamebuilder.conclusion != 'skipped'
run: |
# Each editor is its own reactor, so it stages into its own target dir.
Expand Down Expand Up @@ -335,7 +335,7 @@ jobs:
continue-on-error: true
# always(): central-publishing stages before it uploads, so the tree exists
# even when the Central deploy above failed -- which is precisely the case
# R2 is here to survive. Metadata is regenerated once, after all three.
# R2 is here to survive. Metadata is regenerated once, after all four.
if: always() && steps.deploy_certificatewizard.conclusion != 'skipped'
run: |
# Each editor is its own reactor, so it stages into its own target dir.
Expand Down Expand Up @@ -413,7 +413,7 @@ jobs:
continue-on-error: true
# always(): central-publishing stages before it uploads, so the tree exists
# even when the Central deploy above failed -- which is precisely the case
# R2 is here to survive. Metadata is regenerated once, after all three.
# R2 is here to survive. Metadata is regenerated once, after all four.
if: always() && steps.deploy_settings.conclusion != 'skipped'
run: |
# Each editor is its own reactor, so it stages into its own target dir.
Expand Down Expand Up @@ -447,10 +447,88 @@ jobs:
echo "codenameone-settings ${GITHUB_REF_NAME} never appeared on Maven Central within 30 minutes." >&2
exit 1

# --- GUI Builder editor -------------------------------------------------
# The cn1:guibuilder goal resolves the standalone Java-17 GUI Builder and
# its runtime dependencies from Maven Central, exactly like cn1:settings.
- name: Deploy GUI Builder editor to Maven Central
id: deploy_guibuilder
continue-on-error: true
if: >-
always() && (steps.deploy.outcome == 'success' || steps.confirm.outcome == 'success' ||
steps.r2_core.outcome == 'success')
run: |
export GPG_TTY=$(tty)
# Do not publish this editor to Central unless the core release actually
# reached Central. The editor declares core/plugin at this same version, so
# publishing it against a core that is not there leaves an immutable Central
# release whose dependencies cannot resolve. skipPublishing still stages the
# artifacts, so the R2 upload that follows is unaffected.
skip_publishing=true
if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ] \
&& { [ "${{ steps.deploy.outcome }}" = "success" ] \
|| [ "${{ steps.confirm.outcome }}" = "success" ]; }; then
skip_publishing=false
else
echo "Core release did not reach Central (or dual publish is off):"
echo "staging this editor for R2 only."
fi
cd scripts/guibuilder
xvfb-run -a mvn -Pexecutable-jar -Pguibuilder-central deploy \
-DskipPublishing=$skip_publishing \
-Dcodename1.platform=javase \
-Dgpg.passphrase=$MAVEN_GPG_PASSPHRASE \
-Dcn1.version=$GITHUB_REF_NAME -Dcn1.plugin.version=$GITHUB_REF_NAME \
-Dmaven.test.skip=true
env:
MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}

- name: Publish GUI Builder editor to R2
id: r2_guibuilder
# Non-fatal for the same reason as the core upload: one editor's R2 failure
# must not skip the remaining editors' Central deploys.
continue-on-error: true
# always(): central-publishing stages before it uploads, so the tree exists
# even when the Central deploy above failed -- which is precisely the case
# R2 is here to survive. Metadata is regenerated once, after all four.
if: always() && steps.deploy_guibuilder.conclusion != 'skipped'
run: |
# Each editor is its own reactor, so it stages into its own target dir.
bash maven/scripts/r2/publish-staging-to-r2.sh scripts/guibuilder/target/central-staging
url="${R2_BASE_URL}/com/codenameone/codenameone-guibuilder/${GITHUB_REF_NAME}/codenameone-guibuilder-${GITHUB_REF_NAME}.pom?cb=${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
[ "$code" = "200" ] || { echo "MISSING on R2: codenameone-guibuilder (HTTP $code)" >&2; exit 1; }
echo "ok: codenameone-guibuilder ${GITHUB_REF_NAME} on R2"

- name: Confirm GUI Builder editor on Maven Central
id: confirm_guibuilder
# Runs only to second-guess a failed deploy: central-publishing
# reports "Deployment failed while publishing" for bundles it actually accepted,
# and Central releases are immutable so a rerun cannot repair that.
if: >-
always() && vars.CN1_DUAL_PUBLISH == 'true' &&
steps.deploy_guibuilder.outcome == 'failure'
continue-on-error: true
run: |
set +e
url="https://repo1.maven.org/maven2/com/codenameone/codenameone-guibuilder/${GITHUB_REF_NAME}/codenameone-guibuilder-${GITHUB_REF_NAME}.jar"
for i in $(seq 1 90); do
code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
if [ "$code" = "200" ]; then
echo "Confirmed codenameone-guibuilder ${GITHUB_REF_NAME} on Maven Central"
exit 0
fi
echo "[$i/90] Waiting on Maven Central for codenameone-guibuilder (code=$code)"
sleep 20
done
echo "codenameone-guibuilder ${GITHUB_REF_NAME} never appeared on Maven Central within 30 minutes." >&2
exit 1

- name: Mark the R2 release complete
id: r2_mark_complete
continue-on-error: true
# Only now, once the core reactor and all three editors are up AND the core
# Only now, once the core reactor and all four editors are up AND the core
# artifacts have been observed on R2. This is what makes the tag discoverable:
# regen refuses to advertise a version without it, on this run and every future
# one. r2_core_confirm is what verifies the expected artifacts are actually
Expand All @@ -465,7 +543,8 @@ jobs:
steps.r2_core_confirm.outcome == 'success' &&
steps.r2_gamebuilder.outcome == 'success' &&
steps.r2_certificatewizard.outcome == 'success' &&
steps.r2_settings.outcome == 'success'
steps.r2_settings.outcome == 'success' &&
steps.r2_guibuilder.outcome == 'success'
run: bash maven/scripts/r2/mark-release-complete.sh "${GITHUB_REF_NAME}"

- name: Regenerate R2 metadata and archetype catalog
Expand Down Expand Up @@ -517,6 +596,7 @@ jobs:
bad "${{ steps.r2_gamebuilder.outcome }}" && fail "Game Builder -> R2"
bad "${{ steps.r2_certificatewizard.outcome }}" && fail "Signing Wizard -> R2"
bad "${{ steps.r2_settings.outcome }}" && fail "Settings -> R2"
bad "${{ steps.r2_guibuilder.outcome }}" && fail "GUI Builder -> R2"
bad "${{ steps.r2_mark_complete.outcome }}" && fail "marking the R2 release complete"
bad "${{ steps.r2_metadata.outcome }}" && fail "R2 metadata regeneration"
if [ "${{ vars.CN1_DUAL_PUBLISH }}" = "true" ]; then
Expand All @@ -538,6 +618,10 @@ jobs:
&& [ "${{ steps.confirm_settings.outcome }}" != "success" ]; then
fail "Settings -> Maven Central"
fi
if bad "${{ steps.deploy_guibuilder.outcome }}" \
&& [ "${{ steps.confirm_guibuilder.outcome }}" != "success" ]; then
fail "GUI Builder -> Maven Central"
fi
fi
[ "$status" = "0" ] && echo " all publications succeeded"
exit $status
6 changes: 5 additions & 1 deletion CodenameOne/src/com/codename1/components/SplitPane.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.FontImage;
import com.codename1.ui.Form;
import com.codename1.ui.Graphics;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
Expand Down Expand Up @@ -1094,7 +1095,10 @@ protected boolean isStickyDrag() {
@Override
protected void initComponent() {
super.initComponent();
getComponentForm().setEnableCursors(true);
Form form = getComponentForm();
if (form != null) {
form.setEnableCursors(true);
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5428,8 +5428,9 @@ public String editorPeerQuery(PeerComponent peer, String name, String arg) {

/// Returns true when this platform can bind a `com.codename1.ui.TextInputClient` to a low level text
/// input source (soft keyboard / IME / hardware keyboard) so a component can capture raw text input
/// while rendering the document itself. When false the pure Codename One editors fall back to their
/// `BrowserComponent` backend. The default returns false.
/// while rendering the document itself. When false the pure Codename One editors read the physical
/// keyboard directly through `Component#keyReleased(int)`; there is no browser or HTML backend.
/// The default returns false.
public boolean isTextInputSupported() {
return false;
}
Expand Down
41 changes: 41 additions & 0 deletions CodenameOne/src/com/codename1/ui/CodeEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import com.codename1.ui.editor.CodePureEditor;
import com.codename1.ui.editor.PureEditor;
import com.codename1.ui.editor.SyntaxHighlighter;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.util.EventDispatcher;
import com.codename1.util.SuccessCallback;

import java.util.Hashtable;
Expand Down Expand Up @@ -66,6 +69,7 @@ public class CodeEditor extends AbstractEditorComponent {
private boolean showLineNumbers = true;
private int tabSize = 4;
private CodeCompletionProvider completionProvider;
private final EventDispatcher protectedEditListeners = new EventDispatcher();

/// Creates an empty code editor.
public CodeEditor() {
Expand Down Expand Up @@ -219,6 +223,39 @@ public void insertAtCursor(String text) {
command("insertText", text);
}

/// Protects all text between matching marker lines from editing. This is intended for generated
/// source previews that contain explicit user-editable regions. Passing null clears protection.
public void setProtectedRegionMarkers(String startMarker, String endMarker) {
command("setProtectedMarkers", startMarker == null || endMarker == null
? "" : startMarker + "\n" + endMarker);
}

/// Registers a listener notified when an edit is refused because it falls inside a protected
/// region. Without it a protected region is silent, and one covering most of the document is
/// indistinguishable from an editor that ignores the keyboard. The event source is this editor
/// and the event is fired on the EDT.
///
/// #### Parameters
///
/// - `listener`: invoked once per refused edit
public void addProtectedEditListener(ActionListener listener) {
protectedEditListeners.addListener(listener);
}

/// Removes a listener added by `#addProtectedEditListener(ActionListener)`.
///
/// #### Parameters
///
/// - `listener`: the listener to remove
public void removeProtectedEditListener(ActionListener listener) {
protectedEditListeners.removeListener(listener);
}

/// Moves the caret to a character offset, clamped by the editor backend to the document bounds.
public void setCursorPosition(int offset) {
command("setCursor", String.valueOf(Math.max(0, offset)));
}

/// Retrieves the current caret character offset. The callback is invoked on the EDT.
///
/// #### Parameters
Expand Down Expand Up @@ -303,6 +340,10 @@ void onEditorEvent(String type, String value) {
handleCompletionRequest(value);
return;
}
if ("protectedEdit".equals(type)) {
protectedEditListeners.fireActionEvent(new ActionEvent(this));
return;
}
super.onEditorEvent(type, value);
}

Expand Down
Loading
Loading