ChangedAt returns the time of the most recent observable state change —
text, selection, focus, or composition. Useful for cache invalidation,
autosave throttling, idle detection, and similar change-detection tasks.
The value is strictly monotonically increasing: back-to-back mutations
on coarse-clock platforms are clamped forward by 1ns so each mutation
yields a unique timestamp usable as a cache key.
No-op mutations (SetSelection to the current selection, empty
ReplaceText over a zero-width range, Focus/Blur that don't change focus,
Undo/Redo with nothing to apply) do not advance ChangedAt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The session's state queue buffers input events that arrive between
a commit-end and the next session start, which is needed when multiple
keys are pressed simultaneously (#3382). However, when a text field
was unfocused, keystroke events continued to be queued by the platform
layer and were replayed when a field was next focused, causing
unexpected text to appear.
Clear the queued states in Field.cleanUp(), which is called when a
field is blurred. This preserves the queue for simultaneous keypresses
within a focused field while discarding stale input from unfocused
periods.
Also refactor textInput to move the session field and theTextInput
variable to the cross-platform textinput.go, with each platform
defining only a textInputImpl with platform-specific fields.
Updates #3382Closes#3429
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename all objc.Class variable names to use underscore-separated
convention matching the ObjC class names: class_NSFoo for "NSFoo",
class_GCController for "GCController", etc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename all objc.SEL variable names to use underscore-separated
convention matching the ObjC selector names: sel_fooBar for
"fooBar", sel_fooBar_bazQux for "fooBar:bazQux:".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the per-frame HandleInputWithBounds call with SetBounds (call only
when bounds change) and Handled (query whether input was consumed). Input
processing now runs automatically via an internal hook on before-update,
so users no longer need to drive it every tick.
Deprecate HandleInput and HandleInputWithBounds in favor of the new API.
Closes#3420
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This reverts commit fe93e453bb.
Reason: explicit reseetting would make more sense.
For example, with the current commit, a text input's first input by
a user is treated as the initial state, and this is not expected.
Updates guigui-gui/guigui#279
When an IME starts, the key events can be fired like this:
1. `keydown` code=KeyA isComposing=false
2. `compositionstart`
3. `keyup` code=KeyA isComposing=true
Before this change, KeyA's keyup event was not treated in this case,
and the key was considered to be pressed forever.
This change fixes this issue by always treating keyup events even
if isComposing=true.
Closes#3328
macOS requires the information of the current selection to implement
IME correctly, then the user must use Field. Field is no longer just
a wrapper API.
As this package is experimental, it is OK to have a breaking change,
though this is not ideal.
Updates #3233