Skip to content

Commit 9ccdd79

Browse files
author
Roy Lin
committed
feat: add self-drawn collection drop delegates
1 parent c38366a commit 9ccdd79

37 files changed

Lines changed: 2004 additions & 256 deletions

README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,11 @@ versioned [component matrix](docs/react-aria-component-matrix.json) pins
6969
- `Button` currently has scene/software-pixel smoke evidence through the shared
7070
calculator, but no component is self-drawn conformant yet;
7171
- the shared pointer/keyboard drag/drop state machine now covers multi-type,
72-
wildcard, multi-item, and per-format payload negotiation for
73-
draggable/drop-target authoring, but collection delegates, external
74-
transfer, pixels, accessibility, and real hosts still keep `DropZone` below
72+
wildcard, multi-item, and per-format payload negotiation plus layout-driven
73+
collection `root` and keyed `before`/`on`/`after` targets. Selected
74+
collection items drag as one session, and ListBox, GridList, Tree, and Table
75+
share the same RSX contract; external transfer, reorder/move policy, pixels,
76+
accessibility, and real hosts still keep every affected family below
7577
conformance;
7678
- Checkbox, Radio, and Switch Field/Button parts plus ToastList and
7779
ToastContent are recorded as eight explicit API gaps;
@@ -304,9 +306,12 @@ independently.
304306
scheduled long press, incremental captured move, and self-drawn drag/drop
305307
routing over `PlatformElementId`; drag sessions retain multiple text items
306308
and every MIME/custom representation, filter compatible target items,
307-
negotiate wildcards plus copy/move/link/cancel operations, and share
308-
event-loop deadlines, stable action bubbling, keyed reconciliation, and
309-
reducer rollback in one staged interaction session
309+
negotiate wildcards plus copy/move/link/cancel operations, aggregate stable
310+
selected collection keys, and resolve root/item/insertion targets from
311+
self-drawn layout geometry. A collection is one keyboard Tab stop during a
312+
drag, with arrow/Home/End navigation inside it; all paths share event-loop
313+
deadlines, stable action bubbling, keyed reconciliation, and reducer
314+
rollback in one staged interaction session
310315
- an identical-frame fast path that performs no layout, scene, host, or
311316
presentation work, plus semantic-only commits that skip pixel presentation
312317
- a software Graphics presenter and interactive shared
@@ -322,11 +327,11 @@ independently.
322327
| M1 · GUI integration | Complete | Pinned Graphics boundary, semantic-only dependency gate, renderer inventory, reference/GPU wrappers, first generic adapter |
323328
| M2 · GPU backend | Implementation landed | Graphics commit `8748fab`; Metal and Vulkan CI parity evidence remains |
324329
| M3 · Layout and Scene | Current | Generic calculator rectangle slice landed; full flex, stacking, redraw scheduling, cross-platform fingerprints, and thin-host presentation remain |
325-
| M4 · Text and interaction cutover | In progress | Stable-id raw input, long press, move, and drag/drop foundations landed; shaping, glyphs, editing/IME, accessibility bridges, overlays, and complete calculator scenarios remain |
330+
| M4 · Text and interaction cutover | In progress | Stable-id raw input, long press, move, typed and collection drag/drop foundations landed; shaping, glyphs, editing/IME, accessibility bridges, overlays, and complete calculator scenarios remain |
326331
| M5 · Default cutover | Planned | Make self-drawn content the default, then delete the three legacy widget renderers |
327332
| H0-H5 · Thin platform hosts | H0 complete; H1 in progress | Atomic frames, lifecycle recovery, stable-id raw input/reducers, long press, captured move, typed drag/drop negotiation, zero-toolkit firewalls, and an interactive calculator landed; the Graphics raw-surface edge remains |
328333
| T0-T5 · TSX native authoring | Proposed | Automatic JSX runtime, versioned Node-to-host session, state/event runtime, self-drawn native window, packages, and stable SDK |
329-
| M6-M8 · React Aria components | Catalog pinned; conformance planned | 51/51 families mapped, eight public parts explicitly missing, Button scene smoke only; full software and three-OS self-drawn evidence required |
334+
| M6-M8 · React Aria components | Catalog pinned; conformance planned | 51/51 families mapped; collection DnD authoring/behavior slice landed; eight public parts, full software, accessibility, and three-OS self-drawn evidence remain |
330335

331336
The dependency-ordered plan and acceptance gates are in the
332337
[delivery roadmap](docs/roadmap.md).

docs/platform-hosts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ Landed evidence:
396396
- reducer errors restore the staged interaction state and sequence before the
397397
event is exposed as successful; successful frame reconciliation preserves
398398
focused stable ids, while rejected frames do not touch them
399-
- 48 focused runtime/software tests and four recursive feature/source
399+
- 53 focused runtime/software tests and four recursive feature/source
400400
firewall tests pass without any legacy renderer or OS toolkit dependency
401401
- `self_drawn_calculator` reproduces layout fingerprint
402402
`16529597026056060935`, scene fingerprint `2100550662756266801`, and

docs/react-aria-native.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,22 @@ The first shared interaction milestone is available in the portable runtime:
149149
representations. Keyboard Tab cycles compatible targets only; Enter drops
150150
and Escape cancels. Source/target state, keyed reconciliation, and reducer
151151
rollback remain one transaction.
152+
- Selected draggable collection items now aggregate their stable keys, typed
153+
payloads, and `data-[dragging]` state into one session. A layout-backed
154+
delegate resolves `{type: root}` and keyed `before`/`on`/`after` targets,
155+
filters every target item by accepted type, and exposes `draggingKeys`,
156+
`target`, and `isInternal` in the shared context. During keyboard drag, each
157+
collection is one Tab stop and Arrow/Home/End keys navigate its internal
158+
targets.
152159
- `use_drag`, `use_drop`, `UiDraggable`, `UiDroppable`, and `UiDropZone` lower
153160
their source/target metadata and focusable keyboard affordances into that
154-
shared runtime. Collection item/between/root delegates, external files and
155-
directories, cross-application transfer, and drag previews remain separate
156-
M7 work, so the DropZone family is not yet marked conformant.
161+
shared runtime. ListBox, GridList, Tree, and Table roots additionally lower
162+
`onRootDrop`, `onItemDrop`, `onInsert`, and low-level `onDrop` precedence;
163+
their items/rows and explicit DropIndicator parts use the same self-drawn
164+
target model. Reorder/move policy, dynamic acceptance, external files and
165+
directories, cross-application transfer, drag previews, and conformance
166+
evidence remain separate M7 work, so no affected family is marked
167+
conformant.
157168

158169
- `NativeInputModality` represents keyboard, mouse, touch, pen, virtual, and
159170
unknown input.
@@ -587,7 +598,7 @@ props:
587598
| Priority | Area | Required outcome |
588599
| --- | --- | --- |
589600
| P0 | Self-drawn component accounting | Keep all 51 React Aria 1.19.0 families in the executable matrix, implement the eight recorded public-part gaps, and require every upstream catalog delta to update code, matrix, tests, and milestones together. |
590-
| P0 | Shared self-drawn interaction | Extend the landed stable-id pointer, keyboard, Tab-focus, hover, press, scheduled generic long press, incremental captured move, typed source/target drag negotiation, cancellation, wheel, bubbling, and reducer path with collection item/between/root drag delegates, external file/directory transfer, drag previews, collection long-press selection mode, focus-scope restoration, overlay gestures, text editing/IME, and accessibility activation. |
601+
| P0 | Shared self-drawn interaction | Extend the landed stable-id pointer, keyboard, Tab-focus, hover, press, scheduled generic long press, incremental captured move, typed source/target drag negotiation, collection item/between/root delegates, cancellation, wheel, bubbling, and reducer path with reorder/move policy, dynamic item acceptance, external file/directory transfer, drag previews, collection long-press selection mode, focus-scope restoration, overlay gestures, text editing/IME, and accessibility activation. |
591602
| P0 | Native input conformance | WinUI's complete 98-case V1 manifest passes real OS automation. Populate the AppKit and GTK4 manifests with platform-run mouse, pen, touch where applicable, keyboard, assistive activation, disabled, cancellation, and keyed-rerender fixtures for every role currently marked native; then close or retain evidence-backed menu/item exceptions. |
592603
| P1 | Event propagation | Add platform-run conformance fixtures for conditional `Stop`/`Continue` across nested native controls. |
593604
| P1 | Focus management | Add platform-run conformance fixtures for post-mount `autoFocus`, nested containment, and restoration. |

docs/roadmap.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,17 @@ Landed evidence:
510510
representation; target callbacks receive only matching items without losing
511511
the other representations on those items, while legacy `dragType` plus
512512
`dragValue` normalizes to one compatible text item
513+
- collection drag sources aggregate the stable keys, payloads, and visual
514+
dragging state of selected draggable items. The layout-backed delegate emits
515+
React Aria-shaped root and keyed before/on/after targets, routes
516+
`onRootDrop`/`onItemDrop`/`onInsert` with low-level `onDrop` precedence, and
517+
treats a collection as one Tab target with arrow/Home/End navigation inside
518+
it. ListBox, GridList, Tree, Table, and explicit DropIndicator authoring all
519+
lower to this shared self-drawn path
513520
- the shared 410x620 calculator preserves its reviewed layout and scene
514521
fingerprints, routes eight fake-host events through four reducer actions,
515522
commits the resulting frames, and reaches display value `10`
516-
- 48 focused runtime/software tests plus four recursive H1 firewall tests are
523+
- 53 focused runtime/software tests plus four recursive H1 firewall tests are
517524
included in `just verify`
518525

519526
Remaining H1 work:
@@ -522,10 +529,10 @@ Remaining H1 work:
522529
pinned Graphics commit `8748fab` owns only a surface-independent texture and
523530
readback today, so its safe host-owned surface attachment/recovery contract
524531
must land before GUI can implement this edge without duplicating `wgpu`
525-
- collection item/between/root drag delegates, native file/directory and
526-
cross-application transfer, drag previews, text editing, IME, overlay
527-
gestures, and component-specific interaction conformance remain explicit M4
528-
and M6-M8 work
532+
- collection reorder/move policy and acceptance callbacks, native
533+
file/directory and cross-application transfer, drag previews, text editing,
534+
IME, overlay gestures, and component-specific pixel/accessibility/real-host
535+
conformance remain explicit M4 and M6-M8 work
529536

530537
Gates:
531538

@@ -735,6 +742,9 @@ self-drawn conformance.
735742
- close the executable 1.19.0 behavior deltas: embedded-control keyboard
736743
navigation for GridList/Tree, Menu action key plus value, arbitrary Popover
737744
target rectangles, and multi-MIME/wildcard drag type negotiation
745+
- build on the landed shared collection root/item/insertion delegate with
746+
reorder, cross-collection move, dynamic item acceptance, drag previews, and
747+
software/accessibility/three-host conformance stories
738748

739749
### M8 - Date, color, tables, and advanced data
740750

docs/rsx.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,26 @@ Target callbacks receive only matching items, with every representation on
597597
those items preserved. `UiDraggable`, `UiDroppable`, and `UiDropZone` include a
598598
keyboard-focusable affordance: Enter starts or commits a drag, Tab cycles
599599
compatible targets, and Escape cancels. Pointer and keyboard callbacks receive
600-
the same typed transfer context. External OS files/directories and collection
601-
before/after insertion targets remain separate work.
600+
the same typed transfer context.
601+
602+
`UiListBox`, `UiGridList`, `UiTree`, and `UiTable` also expose the shared
603+
collection contract. Their roots accept `onRootDrop`, `onItemDrop`, `onInsert`,
604+
the low-level overriding `onDrop`, lifecycle handlers, `acceptedDragTypes`,
605+
`dropOperation`, `allowedDropOperations`, and `dropOrientation`. Item/row
606+
components accept a stable `id` plus `isDraggable`, `dragType`, `dragValue`,
607+
and `dragItems`; `UiDropIndicator.targetKey` references that `id`.
608+
Dragging a selected item aggregates all selected draggable item keys and
609+
payloads; dragging an unselected item keeps a single-item session.
610+
611+
Collection callback context adds `draggingKeys`, `isInternal`, and a `target`
612+
descriptor shaped as `{type: "root"}` or
613+
`{type: "item", key, dropPosition: "before" | "on" | "after"}`. Pointer
614+
delegation derives targets from the committed self-drawn boxes. During a
615+
keyboard drag, Tab visits the collection once and Arrow/Home/End keys navigate
616+
inside it. `UiDropIndicator targetKey="..." dropPosition="before|after"`
617+
provides a stable explicit insertion visual. External OS files/directories,
618+
cross-application transfer, drag previews, reorder/move policy, and final
619+
accessibility/host conformance remain separate work.
602620

603621
Runtime lifecycle and effects:
604622

src/compiler.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2888,6 +2888,14 @@ fn drop_indicator_scope_value(props: &CompiledProps) -> GuiResult<JsonValue> {
28882888
use_drop_indicator_value(
28892889
UseDropIndicatorProps::new()
28902890
.orientation(orientation_attribute_value(props))
2891+
.target_key(non_empty_attribute(
2892+
props,
2893+
&["targetKey", "data-drop-target-key"],
2894+
))
2895+
.drop_position(non_empty_attribute(
2896+
props,
2897+
&["dropPosition", "data-drop-position"],
2898+
))
28912899
.target(
28922900
bool_attribute_value(props, &["isTarget", "target", "data-target"])
28932901
.unwrap_or(false),

src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,12 @@ pub use platform_host::{
207207
#[cfg(feature = "platform-runtime")]
208208
pub use platform_runtime::{
209209
PlatformRenderFrame, PlatformScenePresenter, RecordingPreparedFrame, RecordingScenePresenter,
210-
SelfDrawnActionInvocation, SelfDrawnActionPropagation, SelfDrawnDragContext, SelfDrawnDropItem,
211-
SelfDrawnDropOperation, SelfDrawnElementInteraction, SelfDrawnEventContext,
212-
SelfDrawnFrameCommit, SelfDrawnFrameCommitStatus, SelfDrawnFrameSnapshot,
213-
SelfDrawnHostEventOutcome, SelfDrawnInputDispatch, SelfDrawnInteractionChange,
214-
SelfDrawnRuntimeStats, SelfDrawnWindowRuntime,
210+
SelfDrawnActionInvocation, SelfDrawnActionPropagation, SelfDrawnCollectionDropTarget,
211+
SelfDrawnDragContext, SelfDrawnDropItem, SelfDrawnDropOperation, SelfDrawnDropPosition,
212+
SelfDrawnElementInteraction, SelfDrawnEventContext, SelfDrawnFrameCommit,
213+
SelfDrawnFrameCommitStatus, SelfDrawnFrameSnapshot, SelfDrawnHostEventOutcome,
214+
SelfDrawnInputDispatch, SelfDrawnInteractionChange, SelfDrawnRuntimeStats,
215+
SelfDrawnWindowRuntime,
215216
};
216217
#[cfg(all(feature = "platform-runtime", feature = "software-reference"))]
217218
pub use platform_runtime::{

0 commit comments

Comments
 (0)