Skip to content

feat: add IME composition to TextField - #228

Draft
besmpl wants to merge 3 commits into
gogpu:mainfrom
besmpl:agent/p4u-ime
Draft

besmpl wants to merge 3 commits into
gogpu:mainfrom
besmpl:agent/p4u-ime

Conversation

@besmpl

@besmpl besmpl commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add backend-neutral IME events and bridge optional gpucontext v2 controllers into UI windows
  • keep TextField preedit separate from committed text, render marked ranges/underline/cursor geometry, and publish candidate cursor rectangles
  • support UTF-8-safe delete-surrounding, exactly-once commit echo suppression, focus/cancel/disabled ordering, and legacy fallback
  • prevent password/sensitive fields from enabling composition, accepting IME mutations, or publishing surrounding text
  • harden stale callbacks, duplicate ends, visibility/enabled/root changes, controller capability discovery, and teardown

Dependencies

This PR depends on:

  1. gpucontext #30, the optional versioned IME interface contract.
  2. gogpu #462, the integrated platform IME event/controller implementation.

The branch contains no committed replace directives and intentionally remains draft until both dependencies are merged and released. After releases are available, update go.mod, run go mod tidy, rerun the matrix below, and mark this PR ready.

Verification

Dependency validation used temporary modfile replacements only; none are committed.

  • focused app/TextField/event tests: PASS
  • go test -count=1 ./...: PASS
  • focused changed IME race tests: PASS
  • go build ./... and focused go vet: PASS
  • supported Linux/Windows/macOS/browser builds and test compilation: PASS
  • gofmt and git diff --check: PASS

The full app race suite still reproduces the pre-existing animation-pumper mock RequestRedraw race outside this diff. Live native compositor/locale testing requires target hosts.

Refs gogpu/gogpu#331. Depends on gogpu/gpucontext#30 and gogpu/gogpu#462.

@kolkov

kolkov commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

@besmpl, this has sat here without a review from us for almost three months. Sorry about that — the IME contract it depends on was still open on our side, and the PR waited on it instead of us telling you where it stood.

The behaviour you built is what we want: preedit kept apart from committed text, marked ranges and underline, the candidate cursor rect, UTF-8-safe delete-surrounding, and password and sensitive fields kept out of composition.

The contract is settled now and released as gpucontext v0.32.0 (IMEController, IMEState, IMETextRange). What's left is where the types live, as below.

  1. The event package shouldn't import gpucontext. Every other ui event carries ui-owned types, and app/event_bridge translates from gpucontext for keys and mouse. IME should work the same way: ui-owned composition and delete-surrounding types in event, converted in the bridge. Then ui's event API doesn't move each time the platform contract does.
  2. One constructor per event, as for the other events: NewIMECompositionUpdateEvent plus a ...WithTime variant where it's needed, without the six short aliases.
  3. Rather than textfield.Widget returning gpucontext types, a small ui-side text-input-client interface with ui types that the focused widget implements and app.Window queries. That's the Chromium TextInputClient shape. PaintComposition can stay unexported until a second widget needs it.
  4. The bridge then type-asserts the single gpucontext IME contract as released in v0.32.0, with no V2 set or version discovery.
  5. fix(textfield): place the caret from the shaped glyph run #236 merged in v0.1.55, so the preedit width and caret come from the same shaped offsets (widget.TextCaretPositioner) instead of a separate MeasureText of the composition.

gogpu's platform IME backends aren't in yet, so the widget side will be built against the contract with a test double and wired end to end when they land.

Rather than make you wait any longer, we'll do this rework ourselves on your branch. Your authorship stays, and the changelog credits you. If you'd rather pick it back up yourself, say so and we'll hold.

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