mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-09-26 20:11:28 +08:00
@@ -135,9 +135,11 @@ func (t *TextField) Update() error {
|
||||
x, y := t.bounds.Min.X, t.bounds.Min.Y
|
||||
cx, cy := t.cursorPos()
|
||||
px, py := textFieldPadding()
|
||||
x += cx + px
|
||||
y += cy + py + int(fontFace.Metrics().HAscent)
|
||||
handled, err := t.field.HandleInput(x, y)
|
||||
x0 := x + cx + px
|
||||
x1 := x + cx + px + 6 // To be exact, this should be the width of the current character.
|
||||
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
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package textinput
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -99,7 +100,21 @@ type Field struct {
|
||||
// If HandleInput returns true, a Field user should not handle further input events.
|
||||
//
|
||||
// HandleInput returns an error when handling input causes an error.
|
||||
//
|
||||
// Deprecated: use HandleInputWithBounds instead.
|
||||
func (f *Field) HandleInput(x, y int) (handled bool, err error) {
|
||||
return f.HandleInputWithBounds(image.Rect(x, y, x+1, y+1))
|
||||
}
|
||||
|
||||
// HandleInputWithBounds updates the field state.
|
||||
// HandleInputWithBounds must be called every tick, i.e., every Update, when Field is focused.
|
||||
// HandleInputWithBounds takes a character bounds, which decides the position where an IME window is shown if needed.
|
||||
//
|
||||
// HandleInputWithBounds returns whether the text inputting is handled or not.
|
||||
// If HandleInputWithBounds returns true, a Field user should not handle further input events.
|
||||
//
|
||||
// HandleInputWithBounds returns an error when handling input causes an error.
|
||||
func (f *Field) HandleInputWithBounds(bounds image.Rectangle) (handled bool, err error) {
|
||||
if f.err != nil {
|
||||
return false, f.err
|
||||
}
|
||||
@@ -113,7 +128,7 @@ func (f *Field) HandleInput(x, y int) (handled bool, err error) {
|
||||
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(x, y)
|
||||
f.ch, f.end = start(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
|
||||
|
@@ -19,6 +19,7 @@
|
||||
package textinput
|
||||
|
||||
import (
|
||||
"image"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
|
||||
@@ -26,8 +27,6 @@ import (
|
||||
)
|
||||
|
||||
// textInputState represents the current state of text inputting.
|
||||
//
|
||||
// textInputState is the low-level API. For most use cases, Field is easier to use.
|
||||
type textInputState struct {
|
||||
// Text represents the current inputting text.
|
||||
Text string
|
||||
@@ -58,12 +57,11 @@ type textInputState struct {
|
||||
// start starts text inputting.
|
||||
// start returns a channel to send the state repeatedly, and a function to end the text inputting.
|
||||
//
|
||||
// start is the low-level API. For most use cases, Field is easier to use.
|
||||
//
|
||||
// start returns nil and nil if the current environment doesn't support this package.
|
||||
func start(x, y int) (states <-chan textInputState, close func()) {
|
||||
cx, cy := ui.Get().LogicalPositionToClientPositionInNativePixels(float64(x), float64(y))
|
||||
return theTextInput.Start(int(cx), int(cy))
|
||||
func start(bounds image.Rectangle) (states <-chan textInputState, close func()) {
|
||||
cMinX, cMinY := ui.Get().LogicalPositionToClientPositionInNativePixels(float64(bounds.Min.X), float64(bounds.Min.Y))
|
||||
cMaxX, cMaxY := ui.Get().LogicalPositionToClientPositionInNativePixels(float64(bounds.Max.X), float64(bounds.Max.Y))
|
||||
return theTextInput.Start(image.Rect(int(cMinX), int(cMinY), int(cMaxX), int(cMaxY)))
|
||||
}
|
||||
|
||||
func convertUTF16CountToByteCount(text string, c int) int {
|
||||
|
@@ -23,6 +23,8 @@ package textinput
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/ebitengine/purego/objc"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
@@ -118,9 +120,9 @@ type textInput struct {
|
||||
|
||||
var theTextInput textInput
|
||||
|
||||
func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
func (t *textInput) Start(bounds image.Rectangle) (<-chan textInputState, func()) {
|
||||
ui.Get().RunOnMainThread(func() {
|
||||
t.start(x, y)
|
||||
t.start(bounds)
|
||||
})
|
||||
return t.session.ch, t.session.end
|
||||
}
|
||||
@@ -193,7 +195,7 @@ type nsRect struct {
|
||||
size nsSize
|
||||
}
|
||||
|
||||
func (t *textInput) start(x, y int) {
|
||||
func (t *textInput) start(bounds image.Rectangle) {
|
||||
t.endIfNeeded()
|
||||
|
||||
tc := getTextInputClient()
|
||||
@@ -203,10 +205,11 @@ func (t *textInput) start(x, y int) {
|
||||
window.Send(selMakeFirstResponder, tc)
|
||||
|
||||
r := objc.Send[nsRect](contentView, selFrame)
|
||||
y = int(r.size.height) - y - 4
|
||||
// The Y dirction is upward in the Cocoa coordinate system.
|
||||
y := int(r.size.height) - bounds.Max.Y
|
||||
tc.Send(selSetFrame, nsRect{
|
||||
origin: nsPoint{float64(x), float64(y)},
|
||||
size: nsSize{1, 1},
|
||||
origin: nsPoint{float64(bounds.Min.X), float64(y)},
|
||||
size: nsSize{float64(bounds.Dx()), float64(bounds.Dy())},
|
||||
})
|
||||
|
||||
session := newSession()
|
||||
|
@@ -16,6 +16,7 @@ package textinput
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
@@ -156,7 +157,7 @@ body.addEventListener("keyup", handler);`)
|
||||
// TODO: What about other events like wheel?
|
||||
}
|
||||
|
||||
func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
func (t *textInput) Start(bounds image.Rectangle) (<-chan textInputState, func()) {
|
||||
if !t.textareaElement.Truthy() {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -177,8 +178,12 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
t.textareaElement.Set("value", "")
|
||||
t.textareaElement.Call("focus")
|
||||
style := t.textareaElement.Get("style")
|
||||
style.Set("left", fmt.Sprintf("%dpx", x))
|
||||
style.Set("top", fmt.Sprintf("%dpx", y))
|
||||
style.Set("left", fmt.Sprintf("%dpx", bounds.Min.X))
|
||||
style.Set("top", fmt.Sprintf("%dpx", bounds.Min.Y))
|
||||
style.Set("width", fmt.Sprintf("%dpx", bounds.Dx()))
|
||||
style.Set("height", fmt.Sprintf("%dpx", bounds.Dy()))
|
||||
style.Set("font-size", fmt.Sprintf("%dpx", bounds.Dy()))
|
||||
style.Set("line-height", fmt.Sprintf("%dpx", bounds.Dy()))
|
||||
|
||||
if t.session == nil {
|
||||
s := newSession()
|
||||
@@ -200,8 +205,8 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
|
||||
// On iOS Safari, `focus` works only in user-interaction events (#2898).
|
||||
// Assuming Start is called every tick, defer the starting process to the next user-interaction event.
|
||||
js.Global().Get("window").Set("_ebitengine_textinput_x", x)
|
||||
js.Global().Get("window").Set("_ebitengine_textinput_y", y)
|
||||
js.Global().Get("window").Set("_ebitengine_textinput_x", bounds.Min.X)
|
||||
js.Global().Get("window").Set("_ebitengine_textinput_y", bounds.Max.Y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,8 @@
|
||||
package textinput
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
@@ -27,7 +29,7 @@ type textInput struct {
|
||||
|
||||
var theTextInput textInput
|
||||
|
||||
func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
func (t *textInput) Start(bounds image.Rectangle) (<-chan textInputState, func()) {
|
||||
// AppendInputChars is updated only when the tick is updated.
|
||||
// If the tick is not updated, return nil immediately.
|
||||
tick := ebiten.Tick()
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package textinput
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
@@ -41,7 +42,7 @@ type textInput struct {
|
||||
|
||||
var theTextInput textInput
|
||||
|
||||
func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
func (t *textInput) Start(bounds image.Rectangle) (<-chan textInputState, func()) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -50,7 +51,7 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
var err error
|
||||
ui.Get().RunOnMainThread(func() {
|
||||
t.end()
|
||||
err = t.start(x, y)
|
||||
err = t.start(bounds)
|
||||
session = newSession()
|
||||
t.session = session
|
||||
})
|
||||
@@ -76,7 +77,7 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
||||
}
|
||||
|
||||
// start must be called from the main thread.
|
||||
func (t *textInput) start(x, y int) error {
|
||||
func (t *textInput) start(bounds image.Rectangle) error {
|
||||
if t.err != nil {
|
||||
return t.err
|
||||
}
|
||||
@@ -117,8 +118,14 @@ func (t *textInput) start(x, y int) error {
|
||||
dwIndex: 0,
|
||||
dwStyle: _CFS_CANDIDATEPOS,
|
||||
ptCurrentPos: _POINT{
|
||||
x: int32(x),
|
||||
y: int32(y),
|
||||
x: int32(bounds.Min.X),
|
||||
y: int32(bounds.Max.Y),
|
||||
},
|
||||
rcArea: _RECT{
|
||||
left: int32(bounds.Min.X),
|
||||
top: int32(bounds.Max.Y),
|
||||
right: int32(bounds.Max.X),
|
||||
bottom: int32(bounds.Max.Y),
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
|
Reference in New Issue
Block a user