@@ -11,6 +11,7 @@ import (
1111 "unicode/utf8"
1212
1313 "github.com/hajimehoshi/ebiten/v2"
14+ "github.com/hajimehoshi/ebiten/v2/exp/textinput"
1415 "github.com/hajimehoshi/ebiten/v2/inpututil"
1516)
1617
@@ -41,8 +42,12 @@ func (c *Context) textFieldRaw(buf *string, id widgetID, opt option) (EventHandl
4142
4243 f := c .currentContainer ().textInputTextField (id , true )
4344 if c .focus == id {
45+ // A freshly focused text input field still has its own cursor/selection state.
46+ // Seed that state from the bound string before reading input so typing starts
47+ // after any existing text instead of inserting at the beginning.
48+ focusTextInputField (f , * buf )
49+
4450 // handle text input
45- f .Focus ()
4651 x := bounds .Min .X + c .style ().padding + textWidth (* buf )
4752 y := bounds .Min .Y + lineHeight ()
4853 handled , err := f .HandleInput (x , y )
@@ -58,15 +63,17 @@ func (c *Context) textFieldRaw(buf *string, id widgetID, opt option) (EventHandl
5863 if inpututil .IsKeyJustPressed (ebiten .KeyBackspace ) && len (* buf ) > 0 {
5964 _ , size := utf8 .DecodeLastRuneInString (* buf )
6065 * buf = (* buf )[:len (* buf )- size ]
61- f . SetTextAndSelection ( * buf , len ( * buf ), len ( * buf ) )
66+ setTextInputFieldValue ( f , * buf )
6267 }
6368 if inpututil .IsKeyJustPressed (ebiten .KeyEnter ) {
6469 e = & eventHandler {}
6570 }
6671 }
6772 } else {
6873 if * buf != f .Text () {
69- f .SetTextAndSelection (* buf , len (* buf ), len (* buf ))
74+ // Keep the cached text-input object in sync while it is unfocused so the
75+ // next focus starts from the latest value and with the caret at the end.
76+ setTextInputFieldValue (f , * buf )
7077 }
7178 if wasFocused {
7279 e = & eventHandler {}
@@ -100,13 +107,30 @@ func (c *Context) textFieldRaw(buf *string, id widgetID, opt option) (EventHandl
100107 })
101108}
102109
110+ func focusTextInputField (f * textinput.Field , value string ) {
111+ // Focus() does not rewrite the field's text or selection. If this field is being
112+ // focused for the first time, its selection is still 0,0, so copy in the current
113+ // value first and move the caret to the end.
114+ if ! f .IsFocused () && value != f .Text () {
115+ setTextInputFieldValue (f , value )
116+ }
117+ f .Focus ()
118+ }
119+
120+ func setTextInputFieldValue (f * textinput.Field , value string ) {
121+ // Treat programmatic value changes the same way a user expects to keep typing:
122+ // after loading text, place the caret at the end ready for appending.
123+ f .SetTextAndSelection (value , len (value ), len (value ))
124+ }
125+
103126// SetTextFieldValue sets the value of the current text field.
127+ // The caret is moved to the end of the new text.
104128//
105129// If the last widget is not a text field, this function does nothing.
106130func (c * Context ) SetTextFieldValue (value string ) {
107131 _ = c .wrapEventHandlerAndError (func () (EventHandler , error ) {
108132 if f := c .currentContainer ().textInputTextField (c .currentID , false ); f != nil {
109- f . SetTextAndSelection ( value , 0 , 0 )
133+ setTextInputFieldValue ( f , value )
110134 }
111135 return nil , nil
112136 })
@@ -201,7 +225,7 @@ func (c *Context) numberField(value *int, step int, idPart string, opt option) (
201225 if updated {
202226 buf := fmt .Sprintf ("%d" , * value )
203227 if f := c .currentContainer ().textInputTextField (id , false ); f != nil {
204- f . SetTextAndSelection ( buf , len ( buf ), len ( buf ) )
228+ setTextInputFieldValue ( f , buf )
205229 }
206230 e = & eventHandler {}
207231 }
@@ -277,7 +301,7 @@ func (c *Context) numberFieldF(value *float64, step float64, digits int, idPart
277301 if updated {
278302 buf := formatNumber (* value , digits )
279303 if f := c .currentContainer ().textInputTextField (id , false ); f != nil {
280- f . SetTextAndSelection ( buf , len ( buf ), len ( buf ) )
304+ setTextInputFieldValue ( f , buf )
281305 }
282306 e = & eventHandler {}
283307 }
0 commit comments