Skip to content

[P1-35] Add terminal input, hit-testing, and gesture semantics #41

Description

@phranck

Problem

TUIkit has key handling but no coherent SwiftUI-compatible owner for KeyPress/onKeyPress, pointer hover, hit testing, Gesture, TapGesture, LongPressGesture, DragGesture, and gesture state. Focus #22, shortcuts #33, and menus #5 do not own this event-routing layer.

Proposed solution

  • Introduce per-app keyboard and pointer events plus deterministic cell hit testing on the runtime, event, and surface foundations.
  • Add applicable SwiftUI keyboard, hover, hit-testing, and gesture APIs with exact Swift 6.0 signatures.
  • Implement terminal TapGesture/LongPressGesture/DragGesture and gesture composition/state where cell coordinates and duration have deterministic semantics.
  • Route focus, shortcuts, and commands through the shared input model without duplicate dispatch stacks.
  • Explicitly exclude touch-, pressure-, Pencil-, haptic-, and spatial-only APIs in the compatibility manifest.
  • Own KeyEquivalent, EventModifiers, KeyPress, onKeyPress, hover, ContentShapeKinds/contentShape, hit-testing, Gesture, gesture composition, GestureState, TapGesture, LongPressGesture/onLongPressGesture, and DragGesture. Focus stays in [P1-17] Replace the FocusState collision with SwiftUI-compatible focus APIs #22, KeyboardShortcut in [P2-28] Add terminal accessibility, search, refresh, and application actions #33, and data-transfer modifiers/payloads in [P2-36] Add portable clipboard and data-transfer APIs #42.
  • Migrate the existing incompatible KeyEvent, Key, and onKeyPress APIs to the reviewed surface or mark additive compatibility shims for namespacing/removal in [P2-30] Reduce the public implementation surface and namespace TUI-only APIs #35.
  • Record backend capability and fallback semantics for key-up, repeat, modifier-only events, and legacy/SGR/Kitty mouse protocols instead of synthesizing events the terminal did not report.
  • Classify Xcode-27-only GestureInputKinds and inputKinds overloads, including onLongPressGesture(inputKinds:...), as availability-gated surface; only .pointer maps to terminal mouse input. Keyboard remains in KeyPress/EventModifiers, while touch/Pencil-only cases stay excluded.

Acceptance criteria

  • Representative KeyPress, onKeyPress, onHover, allowsHitTesting, Gesture, TapGesture, LongPressGesture/onLongPressGesture, and DragGesture snippets compile under Swift 6.0, including availability fixtures for the OS-27 inputKinds overload.
  • GestureMask, high-priority, simultaneous, sequenced, and exclusive composition match their reviewed generic and isolation contracts.
  • Nested and overlapping views route input deterministically by committed tree, focus, and hit-test order; resize or removal cancels gesture state.
  • Keyboard and pointer backends are injected per app, Sendable-safe, and testable without a real terminal.
  • Cell-coordinate gesture tests cover Unicode-width layouts, clipping, and macOS/Linux parity.
  • Build, tests, SwiftLint strict, and DocC remain warning-free on macOS and Linux.

Dependencies

Depends on runtime #8, event loop #9, stable identity #10, cell surfaces #11, committed effects #13, core API #17, data flow #18, layout #20, modifiers #21, and focus #22. Issue #34 supplies the minimal Transaction value contract needed by GestureState before this issue completes; its later animation propagation and timing work does not block the remaining P1 scope. Public gesture APIs follow the shared input and hit-test contract without waiting for full P2 animation support.

Parallelization

Keyboard routing and pointer/hit-testing can proceed in separate file groups after the shared event contract. Gesture API/state follows hit testing and can integrate independently from clipboard #42 and document work #43.

Commit structure

  • Split this issue into small, thematic, independently revertible commits wherever the work can remain coherent.
  • Every commit must build and keep its applicable tests/gates green. Do not commit an intentionally failing regression test; land it with the smallest fix.
  • Keep characterization/fixtures, mechanical renames or moves, semantic changes, and documentation/migration updates separate when each step remains green.
  • The issue boundary is not a commit boundary; multiple commits are expected for independently reversible changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestparallel-readyCan proceed independently of the current critical pathpriority: highHigh-impact correctness, compatibility, or SDK completeness work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions