mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-09-27 04:16:23 +08:00
@@ -135,9 +135,11 @@ func (t *TextField) Update() error {
|
|||||||
x, y := t.bounds.Min.X, t.bounds.Min.Y
|
x, y := t.bounds.Min.X, t.bounds.Min.Y
|
||||||
cx, cy := t.cursorPos()
|
cx, cy := t.cursorPos()
|
||||||
px, py := textFieldPadding()
|
px, py := textFieldPadding()
|
||||||
x += cx + px
|
x0 := x + cx + px
|
||||||
y += cy + py + int(fontFace.Metrics().HAscent)
|
x1 := x + cx + px + 6 // To be exact, this should be the width of the current character.
|
||||||
handled, err := t.field.HandleInput(x, y)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,7 +100,21 @@ type Field struct {
|
|||||||
// If HandleInput returns true, a Field user should not handle further input events.
|
// If HandleInput returns true, a Field user should not handle further input events.
|
||||||
//
|
//
|
||||||
// HandleInput returns an error when handling input causes an error.
|
// 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) {
|
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 {
|
if f.err != nil {
|
||||||
return false, f.err
|
return false, f.err
|
||||||
}
|
}
|
||||||
@@ -113,7 +128,7 @@ func (f *Field) HandleInput(x, y int) (handled bool, err error) {
|
|||||||
if f.ch == nil {
|
if f.ch == nil {
|
||||||
// TODO: On iOS Safari, Start doesn't work as expected (#2898).
|
// TODO: On iOS Safari, Start doesn't work as expected (#2898).
|
||||||
// Handle a click event and focus the textarea there.
|
// 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.
|
// Start returns nil for non-supported envrionments, or when unable to start text inputting for some reasons.
|
||||||
if f.ch == nil {
|
if f.ch == nil {
|
||||||
return handled, nil
|
return handled, nil
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -26,8 +27,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// textInputState represents the current state of text inputting.
|
// 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 {
|
type textInputState struct {
|
||||||
// Text represents the current inputting text.
|
// Text represents the current inputting text.
|
||||||
Text string
|
Text string
|
||||||
@@ -58,12 +57,11 @@ type textInputState struct {
|
|||||||
// start starts text inputting.
|
// start starts text inputting.
|
||||||
// start returns a channel to send the state repeatedly, and a function to end the 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.
|
// start returns nil and nil if the current environment doesn't support this package.
|
||||||
func start(x, y int) (states <-chan textInputState, close func()) {
|
func start(bounds image.Rectangle) (states <-chan textInputState, close func()) {
|
||||||
cx, cy := ui.Get().LogicalPositionToClientPositionInNativePixels(float64(x), float64(y))
|
cMinX, cMinY := ui.Get().LogicalPositionToClientPositionInNativePixels(float64(bounds.Min.X), float64(bounds.Min.Y))
|
||||||
return theTextInput.Start(int(cx), int(cy))
|
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 {
|
func convertUTF16CountToByteCount(text string, c int) int {
|
||||||
|
@@ -23,6 +23,8 @@ package textinput
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
"github.com/ebitengine/purego/objc"
|
"github.com/ebitengine/purego/objc"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
@@ -118,9 +120,9 @@ type textInput struct {
|
|||||||
|
|
||||||
var theTextInput textInput
|
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() {
|
ui.Get().RunOnMainThread(func() {
|
||||||
t.start(x, y)
|
t.start(bounds)
|
||||||
})
|
})
|
||||||
return t.session.ch, t.session.end
|
return t.session.ch, t.session.end
|
||||||
}
|
}
|
||||||
@@ -193,7 +195,7 @@ type nsRect struct {
|
|||||||
size nsSize
|
size nsSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *textInput) start(x, y int) {
|
func (t *textInput) start(bounds image.Rectangle) {
|
||||||
t.endIfNeeded()
|
t.endIfNeeded()
|
||||||
|
|
||||||
tc := getTextInputClient()
|
tc := getTextInputClient()
|
||||||
@@ -203,10 +205,11 @@ func (t *textInput) start(x, y int) {
|
|||||||
window.Send(selMakeFirstResponder, tc)
|
window.Send(selMakeFirstResponder, tc)
|
||||||
|
|
||||||
r := objc.Send[nsRect](contentView, selFrame)
|
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{
|
tc.Send(selSetFrame, nsRect{
|
||||||
origin: nsPoint{float64(x), float64(y)},
|
origin: nsPoint{float64(bounds.Min.X), float64(y)},
|
||||||
size: nsSize{1, 1},
|
size: nsSize{float64(bounds.Dx()), float64(bounds.Dy())},
|
||||||
})
|
})
|
||||||
|
|
||||||
session := newSession()
|
session := newSession()
|
||||||
|
@@ -16,6 +16,7 @@ package textinput
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
@@ -156,7 +157,7 @@ body.addEventListener("keyup", handler);`)
|
|||||||
// TODO: What about other events like wheel?
|
// 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() {
|
if !t.textareaElement.Truthy() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -177,8 +178,12 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
|||||||
t.textareaElement.Set("value", "")
|
t.textareaElement.Set("value", "")
|
||||||
t.textareaElement.Call("focus")
|
t.textareaElement.Call("focus")
|
||||||
style := t.textareaElement.Get("style")
|
style := t.textareaElement.Get("style")
|
||||||
style.Set("left", fmt.Sprintf("%dpx", x))
|
style.Set("left", fmt.Sprintf("%dpx", bounds.Min.X))
|
||||||
style.Set("top", fmt.Sprintf("%dpx", y))
|
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 {
|
if t.session == nil {
|
||||||
s := newSession()
|
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).
|
// 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.
|
// 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_x", bounds.Min.X)
|
||||||
js.Global().Get("window").Set("_ebitengine_textinput_y", y)
|
js.Global().Get("window").Set("_ebitengine_textinput_y", bounds.Max.Y)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,6 +17,8 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ type textInput struct {
|
|||||||
|
|
||||||
var theTextInput textInput
|
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.
|
// AppendInputChars is updated only when the tick is updated.
|
||||||
// If the tick is not updated, return nil immediately.
|
// If the tick is not updated, return nil immediately.
|
||||||
tick := ebiten.Tick()
|
tick := ebiten.Tick()
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"image"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ type textInput struct {
|
|||||||
|
|
||||||
var theTextInput textInput
|
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() {
|
if microsoftgdk.IsXbox() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,7 @@ func (t *textInput) Start(x, y int) (<-chan textInputState, func()) {
|
|||||||
var err error
|
var err error
|
||||||
ui.Get().RunOnMainThread(func() {
|
ui.Get().RunOnMainThread(func() {
|
||||||
t.end()
|
t.end()
|
||||||
err = t.start(x, y)
|
err = t.start(bounds)
|
||||||
session = newSession()
|
session = newSession()
|
||||||
t.session = session
|
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.
|
// 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 {
|
if t.err != nil {
|
||||||
return t.err
|
return t.err
|
||||||
}
|
}
|
||||||
@@ -117,8 +118,14 @@ func (t *textInput) start(x, y int) error {
|
|||||||
dwIndex: 0,
|
dwIndex: 0,
|
||||||
dwStyle: _CFS_CANDIDATEPOS,
|
dwStyle: _CFS_CANDIDATEPOS,
|
||||||
ptCurrentPos: _POINT{
|
ptCurrentPos: _POINT{
|
||||||
x: int32(x),
|
x: int32(bounds.Min.X),
|
||||||
y: int32(y),
|
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 {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
Reference in New Issue
Block a user