mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2026-04-22 15:57:15 +08:00
exp/textinput: add Field.SetBounds and Field.Handled, process input via internal hook
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 commit is contained in:
@@ -139,11 +139,8 @@ func (t *TextField) Update() error {
|
||||
x1 := x0 + 1
|
||||
y0 := y + cy + py
|
||||
y1 := y0 + int(fontFace.Metrics().HLineGap+fontFace.Metrics().HAscent+fontFace.Metrics().HDescent)
|
||||
handled, err := t.field.HandleInputWithBounds(image.Rect(x0, y0, x1, y1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if handled {
|
||||
t.field.SetBounds(image.Rect(x0, y0, x1, y1))
|
||||
if t.field.Handled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+52
-2
@@ -19,6 +19,8 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/hook"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -79,6 +81,22 @@ func currentState() (string, int, int, textInputState, bool) {
|
||||
return b.String(), f.selectionStartInBytes, f.selectionEndInBytes, f.state, true
|
||||
}
|
||||
|
||||
func init() {
|
||||
hook.AppendHookOnBeforeUpdate(func() error {
|
||||
theFocusedFieldM.Lock()
|
||||
f := theFocusedField
|
||||
theFocusedFieldM.Unlock()
|
||||
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
handled, err := f.handleInput()
|
||||
f.handled = handled
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// Field is a region accepting text inputting with IME.
|
||||
//
|
||||
// Field is not focused by default. You have to call Focus when you start text inputting.
|
||||
@@ -91,12 +109,32 @@ type Field struct {
|
||||
selectionStartInBytes int
|
||||
selectionEndInBytes int
|
||||
|
||||
bounds image.Rectangle
|
||||
handled bool
|
||||
|
||||
ch <-chan textInputState
|
||||
end func()
|
||||
state textInputState
|
||||
err error
|
||||
}
|
||||
|
||||
// SetBounds sets the bounds used for IME window positioning.
|
||||
// The bounds indicate the character position (e.g., cursor bounds) where the IME window should appear.
|
||||
// The bounds width doesn't matter very much as long as it is greater than 0.
|
||||
// The bounds height should be the text height like a cursor height.
|
||||
//
|
||||
// Call SetBounds when the bounds change, such as when the cursor position updates.
|
||||
// Unlike the deprecated [Field.HandleInputWithBounds], SetBounds does not need to be called every tick.
|
||||
func (f *Field) SetBounds(bounds image.Rectangle) {
|
||||
f.bounds = bounds
|
||||
}
|
||||
|
||||
// Handled reports whether the text inputting was handled in the current tick.
|
||||
// If Handled returns true, a Field user should not handle further input events.
|
||||
func (f *Field) Handled() bool {
|
||||
return f.handled
|
||||
}
|
||||
|
||||
// HandleInput updates the field state.
|
||||
// HandleInput must be called every tick, i.e., every Update, when Field is focused.
|
||||
// HandleInput takes a position where an IME window is shown if needed.
|
||||
@@ -106,7 +144,7 @@ type Field struct {
|
||||
//
|
||||
// HandleInput returns an error when handling input causes an error.
|
||||
//
|
||||
// Deprecated: use HandleInputWithBounds instead.
|
||||
// Deprecated: use [Field.SetBounds] and [Field.Handled] instead.
|
||||
func (f *Field) HandleInput(x, y int) (handled bool, err error) {
|
||||
return f.HandleInputWithBounds(image.Rect(x, y, x+1, y+1))
|
||||
}
|
||||
@@ -121,7 +159,19 @@ func (f *Field) HandleInput(x, y int) (handled bool, err error) {
|
||||
// If HandleInputWithBounds returns true, a Field user should not handle further input events.
|
||||
//
|
||||
// HandleInputWithBounds returns an error when handling input causes an error.
|
||||
//
|
||||
// Deprecated: use [Field.SetBounds] and [Field.Handled] instead.
|
||||
func (f *Field) HandleInputWithBounds(bounds image.Rectangle) (handled bool, err error) {
|
||||
f.bounds = bounds
|
||||
f.handled = false
|
||||
handled, err = f.handleInput()
|
||||
if handled {
|
||||
f.handled = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Field) handleInput() (handled bool, err error) {
|
||||
if f.err != nil {
|
||||
return false, f.err
|
||||
}
|
||||
@@ -135,7 +185,7 @@ func (f *Field) HandleInputWithBounds(bounds image.Rectangle) (handled bool, err
|
||||
if f.ch == nil {
|
||||
// TODO: On iOS Safari, Start doesn't work as expected (#2898).
|
||||
// Handle a click event and focus the textarea there.
|
||||
f.ch, f.end = start(bounds)
|
||||
f.ch, f.end = start(f.bounds)
|
||||
// Start returns nil for non-supported envrionments, or when unable to start text inputting for some reasons.
|
||||
if f.ch == nil {
|
||||
return handled, nil
|
||||
|
||||
Reference in New Issue
Block a user