123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- // Copyright 2023 The Ebitengine Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //go:build !ios
- package textinput
- // #cgo CFLAGS: -x objective-c
- // #cgo LDFLAGS: -framework Cocoa
- import "C"
- import (
- "github.com/ebitengine/purego/objc"
- "github.com/hajimehoshi/ebiten/v2/internal/ui"
- )
- type textInput struct {
- // session must be accessed from the main thread.
- session *session
- }
- var theTextInput textInput
- func (t *textInput) Start(x, y int) (chan State, func()) {
- var session *session
- ui.Get().RunOnMainThread(func() {
- t.end()
- start(x, y)
- session = newSession()
- t.session = session
- })
- return session.ch, session.end
- }
- //export ebitengine_textinput_update
- func ebitengine_textinput_update(text *C.char, start, end C.int, committed C.int) {
- theTextInput.update(C.GoString(text), int(start), int(end), committed != 0)
- }
- func (t *textInput) update(text string, start, end int, committed bool) {
- if t.session != nil {
- startInBytes := convertUTF16CountToByteCount(text, start)
- endInBytes := convertUTF16CountToByteCount(text, end)
- t.session.trySend(State{
- Text: text,
- CompositionSelectionStartInBytes: startInBytes,
- CompositionSelectionEndInBytes: endInBytes,
- Committed: committed,
- })
- }
- if committed {
- t.end()
- }
- }
- //export ebitengine_textinput_end
- func ebitengine_textinput_end() {
- theTextInput.end()
- }
- func (t *textInput) end() {
- if t.session != nil {
- t.session.end()
- t.session = nil
- }
- }
- var (
- selAddSubview = objc.RegisterName("addSubview:")
- selAlloc = objc.RegisterName("alloc")
- selContentView = objc.RegisterName("contentView")
- selFrame = objc.RegisterName("frame")
- selInit = objc.RegisterName("init")
- selMainWindow = objc.RegisterName("mainWindow")
- selMakeFirstResponder = objc.RegisterName("makeFirstResponder:")
- selSetFrame = objc.RegisterName("setFrame:")
- selSharedApplication = objc.RegisterName("sharedApplication")
- idNSApplication = objc.ID(objc.GetClass("NSApplication"))
- )
- var theTextInputClient objc.ID
- func getTextInputClient() objc.ID {
- if theTextInputClient == 0 {
- class := objc.ID(objc.GetClass("TextInputClient"))
- theTextInputClient = class.Send(selAlloc).Send(selInit)
- }
- return theTextInputClient
- }
- type nsPoint struct {
- x float64
- y float64
- }
- type nsSize struct {
- width float64
- height float64
- }
- type nsRect struct {
- origin nsPoint
- size nsSize
- }
- func start(x, y int) {
- t := getTextInputClient()
- window := idNSApplication.Send(selSharedApplication).Send(selMainWindow)
- contentView := window.Send(selContentView)
- contentView.Send(selAddSubview, t)
- window.Send(selMakeFirstResponder, t)
- r := objc.Send[nsRect](contentView, selFrame)
- y = int(r.size.height) - y - 4
- t.Send(selSetFrame, nsRect{
- origin: nsPoint{float64(x), float64(y)},
- size: nsSize{1, 1},
- })
- }
|