Skip to content

Native operating system drag and drop, carrying the clipboard's own payload - #5662

Merged
shai-almog merged 114 commits into
masterfrom
native-os-drag-and-drop
Sep 5, 2026
Merged

shai-almog merged 114 commits into
masterfrom
native-os-drag-and-drop

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Codename One's drag and drop has always been lightweight: setDraggable and setDropTarget move a rendered image around inside one form. It never leaves the application, so it cannot drop a file on the desktop, cannot carry text into another application's window, and cannot receive anything from one.

This adds the other half.

The idea

The payload is a ClipboardContent -- the same object a copy publishes -- because a drag is a copy the user aims with the pointer. Whatever the application can already put on the clipboard it can already drag out, and whatever it can paste it can already accept as a drop. Offering several representations is what lets one drag land correctly in unrelated applications: a text editor takes text/html, a plain text field takes text/plain, and the desktop takes the file list.

Label file = new Label("report.pdf");
file.setNativeDragOperation(NativeDragOperation.createFileDrag(paths));

inbox.setNativeDropTarget(true);
inbox.setAcceptedDropMimeTypes(ClipboardContent.MIME_FILE);
inbox.addNativeDropListener(e -> load(((NativeDropEvent)e).getFiles()));

Core

ClipboardContent gains lazily built representations. That is what makes dragging a file out workable: the drag has to name the file when it starts, but the user may drop it nowhere, so setDataProvider declares the representation without paying for it and the file is written at the moment a receiver reads it. It also gains setFiles/getFiles -- which replaces the String-or-String[] duality every port was open-coding -- and text/uri-list.

NativeDragOperation carries the payload, the allowed actions and the drag image. ACTION_MOVE means the receiver takes ownership and the source deletes its copy; the source only learns whether that happened once the platform has finished, so the outcome arrives through a completion listener rather than from the call that started the drag.

New API, all in com.codename1.ui: NativeDragAndDrop, NativeDragOperation, NativeDropEvent, ClipboardDataProvider, and on Component the drag-source and drop-target pairs. Orthogonal to the existing setDraggable/setDropTarget, which are untouched.

Threading

Drops arrive on the platform's own drag thread. The target is resolved there, from the accepted MIME types and actions alone, and the callbacks run on the event dispatch thread.

That is not fastidiousness. In the JavaSE port the event dispatch thread blocks on the AWT thread to blit every frame, so an AWT callback that waits on the event dispatch thread deadlocks on the first drag. The consequence is that a MIME filter is exact from the first drag event, while a decision made inside a callback reaches the cursor one event later -- a frame. canAcceptNativeDrop is the one method that runs off the event dispatch thread, and says so.

Ports

Port Drags Leaves the app
JavaSE (simulator and "run as desktop app") yes yes -- other windows, the desktop, file managers
Android yes Nougat and later, via DRAG_FLAG_GLOBAL
iPadOS and Mac Catalyst yes yes
iPhone yes no -- nothing on screen to drop into
everything else no no

JavaSE goes through AWT's own drag machinery, so our own window is a drop target for our own drags too. The transferable that publishes a copy now publishes a drag as well; it derives its flavors from the MIME types alone rather than by reading values, which is what keeps a promised file unwritten until the drop.

Android shares the ClipData conversion the clipboard already had rather than growing a second one that would drift from it, including the file provider URIs that let the receiving application read generated bytes.

iOS, iPadOS and Mac Catalyst use UIDragInteraction / UIDropInteraction. UIKit owns the gesture -- its own recognizer decides a drag has begun and then asks what is being dragged -- so the framework stages the operation on the press and the native side announces the session afterwards. The payload is fetched at that later moment, so a drag offering a file the application has not written yet does not write it every time the user merely touches the component.

Where the platform has none of this, NativeDragAndDrop.isSupported() answers false, every call is a no-op, and the lightweight drag and drop is unaffected.

Not covered: the JavaScript port and the native macOS, Windows and Linux ports.

Also fixed

A top level primes drag and drop twice per press -- once on the component under the pointer, once on its nearest draggable ancestor -- and the second pass discarded what the first had staged when the drag source sat between the two. Found while reviewing; covered by a regression test.

Verification

  • 6091 core tests and 327 JavaSE tests green. 17 new core tests, 11 new JavaSE tests covering both transferable conversions, the promised-file path, text/uri-list, target resolution and the action mapping.
  • SpotBugs clean on core-unittests, android and ios. Copyright, control-character, package-info, cast-semantics, native-signature and build-hint gates clean; Vale and LanguageTool clean on the guide.
  • The simulator runs the new sample and reports that drags can leave the application.
  • CN1DragAndDrop.m compiles for real arm64 iOS, for Mac Catalyst and for the macOS stub branch. A full translation of the sample app confirms the new native sources ship and that all five Java callbacks survive dead-code elimination.

Not verified: a physically driven operating system drag. Synthetic mouse input does not reach the window server on the machine this was built on, so a scripted drag proved nothing either way. Android and iOS are compile- and analysis-verified rather than device-run.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-05T09:16:32.312449Z 254fa36 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d480d757f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated
@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@github-actions

github-actions Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ✅ ByteCodeTranslator: 0 findings (no issues)
    • ✅ android: 0 findings (no issues)
    • ✅ build-hint-catalog: 0 findings (no issues)
    • ✅ build-hint-tools: 0 findings (no issues)
    • ✅ codenameone-maven-plugin: 0 findings (no issues)
    • ✅ core-unittests: 0 findings (no issues)
    • ✅ ios: 0 findings (no issues)
  • ✅ PMD: 0 findings (no issues) [Report archive]
  • ✅ Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 9.14% (9065/99176 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.92% (46713/523952), branch 3.51% (1743/49639), complexity 3.50% (1853/52940), method 5.38% (1498/27852), class 10.84% (404/3727)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 9.14% (9065/99176 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.92% (46713/523952), branch 3.51% (1743/49639), complexity 3.50% (1853/52940), method 5.38% (1498/27852), class 10.84% (404/3727)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 188ms / native 132ms = 1.4x speedup
SIMD float-mul (64K x300) java 249ms / native 172ms = 1.4x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 77.000 ms
Base64 CN1 decode 86.000 ms
Base64 native encode 357.000 ms
Base64 encode ratio (CN1/native) 0.216x (78.4% faster)
Base64 native decode 303.000 ms
Base64 decode ratio (CN1/native) 0.284x (71.6% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 160 screenshots: 160 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 217 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD float-mul (64K x300) java 56ms / native 2ms = 28.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 150.000 ms
Base64 CN1 decode 93.000 ms
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.167x (83.3% faster)
Image applyMask (SIMD off) 48.000 ms
Image applyMask (SIMD on) 34.000 ms
Image applyMask ratio (SIMD on/off) 0.708x (29.2% faster)
Image modifyAlpha (SIMD off) 33.000 ms
Image modifyAlpha (SIMD on) 30.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.909x (9.1% faster)
Image modifyAlpha removeColor (SIMD off) 38.000 ms
Image modifyAlpha removeColor (SIMD on) 32.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.842x (15.8% faster)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 739f5d94ad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 243 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 70ms / native 8ms = 8.7x speedup
SIMD float-mul (64K x300) java 53ms / native 2ms = 26.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 158.000 ms
Base64 CN1 decode 88.000 ms
Base64 native encode 469.000 ms
Base64 encode ratio (CN1/native) 0.337x (66.3% faster)
Base64 native decode 189.000 ms
Base64 decode ratio (CN1/native) 0.466x (53.4% faster)
Base64 SIMD encode 46.000 ms
Base64 encode ratio (SIMD/CN1) 0.291x (70.9% faster)
Base64 SIMD decode 42.000 ms
Base64 decode ratio (SIMD/CN1) 0.477x (52.3% faster)
Base64 encode ratio (SIMD/native) 0.098x (90.2% faster)
Base64 decode ratio (SIMD/native) 0.222x (77.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.333x (66.7% faster)
Image applyMask (SIMD off) 60.000 ms
Image applyMask (SIMD on) 49.000 ms
Image applyMask ratio (SIMD on/off) 0.817x (18.3% faster)
Image modifyAlpha (SIMD off) 44.000 ms
Image modifyAlpha (SIMD on) 37.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.841x (15.9% faster)
Image modifyAlpha removeColor (SIMD off) 42.000 ms
Image modifyAlpha removeColor (SIMD on) 38.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.905x (9.5% faster)

@shai-almog

shai-almog commented Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7668b3794a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c8190afaf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee8afadae5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 945cc52052

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Component.java
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c6e6b0377

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 281c900eec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab88b4f47c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Component.java
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab5dc154c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSENativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated
@shai-almog
shai-almog force-pushed the native-os-drag-and-drop branch from ab5dc15 to 737eb52 Compare September 2, 2026 12:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 737eb52c73

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ac7e4326c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
@shai-almog
shai-almog force-pushed the native-os-drag-and-drop branch from 8ac7e43 to 25f144c Compare September 2, 2026 14:33

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25f144c4eb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e819b12260

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/Component.java
Comment thread Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4353c418cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2452f1f542

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f67663b4cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db0c0c034f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 005421dac8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
Comment thread CodenameOne/src/com/codename1/ui/Window.java
… that answered for the wrong drag

A window being hidden, minimized or disposed goes through cancelPendingInput,
which clears the pressed component and the press token and never touched what the
press had staged for the operating system. Only cancelPointerGesture did -- the
other kind of cancellation, an overlay taking the pointer -- so a gesture staged
on a window the user can no longer see was left for the next press that does not
reach initDragAndDrop, or for the platform's own recognizer, to start a drag
carrying that window's payload. It now goes the same way a form's does when the
form is deinitialized, and by the same rule: only what belongs to this surface,
so a window closing does not cancel a gesture staged somewhere still on screen.

The marker for a drop still on its way to its target became a count last time,
and a count answers "is anything pending" to a question that is always about one
particular drag. A second drag that ended without a local drop of its own -- it
was cancelled, or dropped into another application -- found the first drop still
outstanding and skipped its own release, and since that first release names its
own clip, nothing ever let go of the second one: its staged files stayed pinned
until some later drag happened to take the slot. Kept by clip now, which is what
the release beside it was already keyed by.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4c883311e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidNativeDragAndDrop.java Outdated
…ld outlive their exemption

Target resolution walks outwards from the component under the pointer, so the
innermost target wins -- that is what makes a move-only child inside a
copy-capable parent work at all. Hit testing is what decides where the walk
starts, and it promotes a focusable container over a child that is not focusable,
which puts the start above the component the pointer is really over. The descent
added for that ran only when the walk found nothing, so a focusable container
that was *itself* a target simply took the drop meant for its child. The descent
now runs first, inside what hit testing answered with, and the walk outwards
follows it -- which is the order the rest of the resolution already assumes.

An assembly was exempt from reclamation only while it was still growing, and its
caller claims it afterwards. Between the two there is a gap, and a copy on
Android's UI thread and a drag on the event dispatch thread assemble at the same
time: one could finish and be waiting to be claimed while the other's staging
triggered a reclamation that deleted its files, so the caller published, or
dragged, a clip of dead URIs. Every assembly is now exempt until its caller has
taken it over or given up on it, and the drag takes its hold before the assembly
gives its exemption up, so there is no moment when neither holds it.

And ACTION_DRAG_ENDED read the clip slot after queueing the completion. That
listener runs on the event dispatch thread and may start the next drag, which
claims the slot -- so the release let go of a drag that had only just begun. The
ending session's clip is captured before anything is queued, and only that
identity is released.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26371a2df9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
…swer

The release that clears a drag's hold only when it is still that drag's compared
under the lock and then cleared under a second one, and a completion listener on
the event dispatch thread can start the next drag in between -- claiming the slot
the comparison had just found safe to clear. The new drag's files were then
reclaimable while it was still running.

Both halves under one lock now. Nothing about the answer is worth reading if the
thing it is about can change before it is used.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82760494c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/Android/src/com/codename1/impl/android/AndroidImplementation.java Outdated
The staging guard was keyed on the gesture having been offered to the platform,
and UIKit begins its own drag from a long press that need never travel far enough
for this framework to call it a drag at all -- so "offered" was still false at the
moment the guard was there to cover. A second finger pressing another source in
that window took the slot, and the session UIKit was about to begin exported that
component's payload and reported its move against it.

Keyed on the component instead. A press on the component that already owns the
staging may always restage -- the drag is that component's either way, and that
is what lets the next press inherit a gesture the platform dropped on the floor
-- while a press on a different one leaves it alone, offered or not. The release
guard beside it loses the same condition for the same reason, so the second
finger's release no longer ends a gesture it never owned; the release that
arrives with no press in progress still clears the slot, which is what keeps a
staging from outliving every gesture on the surface.

Inbound, a clip that named exactly one type and put its value in the item's text
had that name thrown away: the value was published as text/plain, the type it was
actually advertised under could not be satisfied from anything else, and it
vanished -- so a target filtered to it accepted the hover and was refused the
drop, and a paste got a bare string. The value now goes to the type the clip gave
it, by the same reasoning the single unclaimed URI is paired with the single
unsatisfied type. A clip that advertises text/plain, or more than one type, is
read exactly as it was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2f0f6cc20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
The staging guard was keyed on the component, and a component may hand out a
different operation for every position -- createNativeDragOperation documents
exactly that, for a list giving out the row under the finger. So a second finger
elsewhere on the same component restaged a different payload, and the session the
first finger's long press was about to become would have carried it. Keyed on the
press token now, which is what tells two fingers apart on one component.

That needed the one place where this framework dispatches a press twice for a
single gesture to say so. A press that lands on a momentum-scrolling container is
a "stop the glide" press, and Form defers it to the first drag packet, where it
re-dispatches at *that* position: the re-dispatch mints a token of its own, so
under a token rule it read as a foreign press and the guard kept the original
staging -- with a drag origin two hundred pixels back, which made that very
packet look like a drag and started one. The deferral now cancels what the
original press staged before re-dispatching, which is what it means: the gesture
is being decided again, from where it now is.

Nothing is stranded by keying on the press. Every gesture ends in a release as
far as this framework is concerned -- the iOS recognizer dispatches
pointerReleased from touchesCancelled, and the other ports call gestureCancelled
themselves -- so the press that owns a staging always gets to clear it. The test
that pressed twice at one pixel with nothing in between now releases in between,
which is what the ports actually deliver, and still asserts what it was written
for: the second gesture carries its own payload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45dabd674b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
A top level mints one press token and keeps only the newest, so when the first of
two fingers is the one that lifts, its release arrives carrying the *second*
finger's token. The staging guard added for the other direction then kept what
the departed finger had staged, and the second finger's long press could become a
session carrying it -- the payload of a gesture that was over.

The position settles what the token cannot. A release lands where its own finger
is, and a gesture that had travelled far enough to be somewhere else would have
become a drag rather than sitting staged, so a release at the staged press's
position is that press's release whatever token it carries. Within the same slop
a press uses to become a drag, for the reason the drop's own recovery uses it.

The pair now reads: a press may only take the slot its own press owns, and a
release may only end the staging of the press that made it -- by token where the
token still names it, by position where it cannot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f828da68bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java Outdated
Comment thread Ports/iOSPort/src/com/codename1/impl/ios/IOSImplementation.java Outdated
The recovery for a target whose position moved reads the hover the drag left
behind, and a drop that assembles after its session ended can arrive long after a
*second* session has hovered the same surface. The release point then matched the
newer hover, the newer session's target was still willing, and the older drop's
payload was delivered to a component it was never dropped on.

The hover now carries a generation, and a drop assembled later quotes back what
it saw when the user released: while the number still matches, the hover is that
drop's own, and the moment it does not the recovery is reading somebody else's.
The iOS port takes it in performDrop and hands it to the commit, beside the
action and the action mask it already takes there for exactly this reason -- and
a port with nothing to quote passes NO_HOVER_GENERATION and gets what it always
got.

The iOS drag payload asked its URI list provider twice. declareDraggedUrls
resolved the list off the content to split it into items, and the session's own
memo -- which is what a receiver's later read goes through -- never saw that
answer, so the provider ran again for the first receiver that asked. It now
resolves through the session memo, which is the one place a drag is entitled to
ask, and a provider that fails is remembered as having failed rather than being
asked a second time.

The first version of the test for the hover generation passed with the guard
removed: the second session hovered somewhere else, so the release point never
matched and the recovery was never reached. It now hovers where the first drop
was released, which is the case the finding is about, and it fails without the
guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3806e3f2cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/iOSPort/nativeSources/CN1DragAndDrop.m Outdated
Comment thread CodenameOne/src/com/codename1/ui/NativeDragAndDrop.java
shai-almog and others added 5 commits September 4, 2026 18:43
… operation retired mid-start

The iOS drop budget deleted the oldest copies as soon as a newer drop committed,
and a commit only *queues* the target's callback -- the payload is read inside
it. Two large drops committing before the event dispatch thread has run the first
callback, which happens when that thread is busy or when the callback itself sits
in a nested event loop, had the first drop's copies deleted before its target
ever opened them; on a local move the source then deleted the original too, and
the payload was gone entirely. A drop is now owed to its target until the
callback has returned, and reclamation skips what is owed rather than waiting for
it -- the drops behind it may still be reclaimable. The port records the copies
before the commit rather than after it, because the event dispatch thread can run
that callback, and report the delivery finished, before the calling thread gets
back; recorded afterwards, the report found nothing and the copies were owed for
good.

This is the same shape as the Android hold fixed earlier, in the other port's
words: the release is queued behind the delivery it waits for, and named by the
identity of the thing it releases.

And startDrag read the operation's allowed actions before delivering the
completion its previous session still owed. A listener may say anything about the
operation it is being told about, including that nothing may be done with it any
more, which is how a source retires an instance it reuses -- and the check that
had already passed was about the session before. The port was then asked to start
a session the operation's own contract says must not begin. Checked again after
the completion, and the framework gives the session back rather than starting it,
exactly as it does when the port refuses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
build-macos failed compiling the translated application: two calls to
virtual_java_lang_String_split were undeclared functions, which under
-Werror=implicit-function-declaration is a build failure. ParparVM's
java.lang.String has no split, and the iOS port's Java is translated by it -- the
rest of this file has always split with com.codename1.io.Util for exactly that
reason, five times over. Both of mine now do too.

Nothing local catches this: the port compiles against a full JDK in the Ant and
Maven builds, and it is the translation that has the smaller API. The other
methods these additions use -- charAt, substring, trim, equalsIgnoreCase,
Math.abs -- are all in vm/JavaAPI; split was the only one that is not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven jobs failed to link -- ios, ios-metal, ios-tv, ios-watch, native-ios,
macos and packaging -- on two undefined symbols: CN1UtiForMime and CN1MimeForUti,
referenced from the clipboard natives in IOSNative.m. Those natives are compiled
for every slice; the mapping they call sat below the #if that compiles out the
drag interaction on macOS, watchOS and tvOS, where there is no UIDragInteraction.
The mapping needs none of that -- it is Foundation and the type identifier
database -- so it now lives above the guard, with CoreServices where macOS keeps
what MobileCoreServices holds elsewhere, and availability spelled for all four
platforms rather than iOS alone.

CN1DropDeliveryFinished gets a stub in the guarded branch for the same reason: a
slice that cannot receive a drop has nothing owed to a target, but its native is
compiled all the same and the link needs the symbol.

Only the macOS build ever ran this far before -- the String.split error stopped
compilation short of the link, and fixing that let every one of these through to
it. Compiled and checked locally for all four slices this time, which is what I
should have been doing since the first of these symbols was exported:

    for sdk in iphoneos macosx watchsimulator appletvsimulator; do
      xcrun --sdk $sdk clang -c -fno-objc-arc -I. -isysroot "$(xcrun --sdk $sdk --show-sdk-path)" ...
      nm -g CN1DragAndDrop.o | grep ' T _CN1'
    done

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The chapter had its snippets but no entry under Samples/samples, which is where
every other feature the guide documents keeps something you can actually run --
and for a feature about moving data between applications, running it is the only
way to see it work at all. NativeDragAndDropSample drags text out under three
types at once, so one gesture lands correctly in a rich text editor, a plain text
field and a browser; promises a file that is written only when a receiver asks
for it, which is the case the desktop exercises; and accepts drops into two
zones, one that takes anything and reports what arrived -- the types, whether the
drag came from this application, the action the system settled on -- and one
filtered to files and to a copy, so a file manager offering a move is answered
rather than refused. The hover callbacks light the zone up, since that is what a
drop target uses to say it will take the drag.

The chapter now points at it, the way In-Car-Experiences, Phone-Number-Verification
and External-Surfaces point at theirs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit replaced Samples/samples/NativeDragAndDropSample wholesale on
the belief that it was missing. It was not -- it went in with the feature and was
revised once since -- so that commit deleted a hundred lines of working, reviewed
sample and put a near-equivalent in its place. Restored verbatim.

What stays is the one thing the chapter really lacked: a line pointing at the
sample, the way In-Car-Experiences, Phone-Number-Verification and
External-Surfaces point at theirs. Its wording now describes the sample that
exists rather than the one I had written.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 254fa366a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// still has the image to drag.
String[] files;
try {
files = content.getFiles();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve iOS file lists through the session memo

When the same ClipboardContent is being copied while UIKit starts a drag, this reads the content's shared LazyValue cache rather than the new ExportedDrag memo. The clipboard can therefore populate MIME_FILE after dragSessionStarted() resets it but before this read, causing the drag to reuse the clipboard transfer's generated temporary path instead of invoking the provider for its own session; conversely, the reset can make the concurrent copy invoke its provider twice. Resolve the file value through exported.produce(MIME_FILE) and normalize its String/String[] result, as the URI-list path already does.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 254fa366a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1391 to +1392
NSMutableDictionary* urlItem =
[NSMutableDictionary dictionaryWithObject:url forKey:@"public.url"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Publish iOS file entries as file URLs

When ClipboardContent.MIME_FILE is copied on iOS, every entry is published under public.url, even though this change maps the file representation to public.file-url and detects incoming files by conformance to that identifier in CN1DragAndDrop.m. Since public.file-url conforms to public.url rather than the reverse, file-only paste consumers do not see these items as files; select public.file-url for local file URLs while retaining public.url for web links.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1042 seconds

Build and Run Timing

Metric Duration
Simulator Boot 66000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 6000 ms
Test Execution 587000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 104ms / native 3ms = 34.6x speedup
SIMD float-mul (64K x300) java 164ms / native 3ms = 54.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 224.000 ms
Base64 CN1 decode 222.000 ms
Base64 native encode 596.000 ms
Base64 encode ratio (CN1/native) 0.376x (62.4% faster)
Base64 native decode 405.000 ms
Base64 decode ratio (CN1/native) 0.548x (45.2% faster)
Base64 SIMD encode 123.000 ms
Base64 encode ratio (SIMD/CN1) 0.549x (45.1% faster)
Base64 SIMD decode 101.000 ms
Base64 decode ratio (SIMD/CN1) 0.455x (54.5% faster)
Base64 encode ratio (SIMD/native) 0.206x (79.4% faster)
Base64 decode ratio (SIMD/native) 0.249x (75.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 22.000 ms
Image createMask ratio (SIMD on/off) 2.750x (175.0% slower)
Image applyMask (SIMD off) 70.000 ms
Image applyMask (SIMD on) 78.000 ms
Image applyMask ratio (SIMD on/off) 1.114x (11.4% slower)
Image modifyAlpha (SIMD off) 57.000 ms
Image modifyAlpha (SIMD on) 75.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.316x (31.6% slower)
Image modifyAlpha removeColor (SIMD off) 50.000 ms
Image modifyAlpha removeColor (SIMD on) 50.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.000x (0.0% slower)

@shai-almog

shai-almog commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1133 seconds

Build and Run Timing

Metric Duration
Simulator Boot 90000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 3000 ms
Test Execution 402000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 69ms / native 3ms = 23.0x speedup
SIMD float-mul (64K x300) java 56ms / native 8ms = 7.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 252.000 ms
Base64 CN1 decode 121.000 ms
Base64 native encode 477.000 ms
Base64 encode ratio (CN1/native) 0.528x (47.2% faster)
Base64 native decode 1880.000 ms
Base64 decode ratio (CN1/native) 0.064x (93.6% faster)
Base64 SIMD encode 54.000 ms
Base64 encode ratio (SIMD/CN1) 0.214x (78.6% faster)
Base64 SIMD decode 55.000 ms
Base64 decode ratio (SIMD/CN1) 0.455x (54.5% faster)
Base64 encode ratio (SIMD/native) 0.113x (88.7% faster)
Base64 decode ratio (SIMD/native) 0.029x (97.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 16.000 ms
Image createMask (SIMD on) 5.000 ms
Image createMask ratio (SIMD on/off) 0.313x (68.8% faster)
Image applyMask (SIMD off) 81.000 ms
Image applyMask (SIMD on) 49.000 ms
Image applyMask ratio (SIMD on/off) 0.605x (39.5% faster)
Image modifyAlpha (SIMD off) 86.000 ms
Image modifyAlpha (SIMD on) 197.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.291x (129.1% slower)
Image modifyAlpha removeColor (SIMD off) 86.000 ms
Image modifyAlpha removeColor (SIMD on) 181.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 2.105x (110.5% slower)

@shai-almog
shai-almog merged commit 8b001a8 into master Sep 5, 2026
75 of 76 checks passed
@shai-almog
shai-almog deleted the native-os-drag-and-drop branch September 5, 2026 16:09
shai-almog added a commit that referenced this pull request Sep 11, 2026
…needs (#5776)

ByteCodeTranslator treated UniformTypeIdentifiers.framework as the macOS
spelling of MobileCoreServices and linked it on that branch alone. It is not:
MobileCoreServices carries only the deprecated C functions, while the UTType
class lives in UniformTypeIdentifiers on every platform from iOS 14 / macOS 11 /
watchOS 7 / tvOS 14.

CN1DragAndDrop.m compiles that class into every slice -- its MIME-to-identifier
mapping sits outside the UIKit guard because the clipboard natives need it too
-- so an iOS application referenced _OBJC_CLASS_$_UTType with nothing defining
it, and every device archive failed at the link.

Link it on all slices, weakly: ios.deployment_target reaches back before the
framework existed and a hard link against one the device lacks kills the process
at launch, while the uses are already @available fenced. Classified in
WatchNativeBuilder as linkable, verified present in both watch SDKs.

Why no CI job caught it
-----------------------

Every iOS job was green for five days, and structurally had to be. Nothing
declared the framework there either -- the sample application supplies it by
accident, three ways a plain application does not have:

  - a Swift autolink hint, since the sample has Swift in its app target and
    `import SwiftUI` stamps -framework UniformTypeIdentifiers into the object
  - CLANG_ENABLE_MODULES, which IPhoneBuilder turns on for the Metal build, so
    the same source links there and fails on the GL one
  - CocoaPods dependencies on the link line

scripts/check-ios-framework-links.py closes that. It runs over the project
build-ios-app.sh has already generated, compiles the port's natives with the
project's own settings, and resolves their undefined symbols against the
frameworks the project DECLARES -- never against an autolink hint, which is a
load command rather than a definition and so cannot hide a symbol from nm. A
native that fails to compile is fatal rather than skipped, and the SDK symbol
index self-tests, because both of those failure modes report success.

Verified: with the post-#5662 source injected into a project that does not
declare the framework the gate fails naming UniformTypeIdentifiers.framework and
_OBJC_CLASS_$_UTType; declaring it on the app target makes the gate pass. 79 of
79 port natives compile, 623 SDK symbols examined. The link itself was A/B'd
with the real CN1DragAndDrop.m for arm64-apple-ios15.0: undefined without the
framework, resolved with -weak_framework UniformTypeIdentifiers.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants