diff --git a/app/internal/window/os_android.go b/app/internal/window/os_android.go index 67f168f9..6fa166c1 100644 --- a/app/internal/window/os_android.go +++ b/app/internal/window/os_android.go @@ -71,11 +71,20 @@ type window struct { stage system.Stage started bool + state, newState windowState + + // mu protects the fields following it. mu sync.Mutex win *C.ANativeWindow animating bool } +// windowState tracks the View or Activity specific state lost when Android +// re-creates our Activity. +type windowState struct { + cursor *pointer.CursorName +} + // gioView hold cached JNI methods for GioView. var gioView struct { once sync.Once @@ -118,8 +127,12 @@ var android struct { mwakeupMainThread C.jmethodID } +// view maps from GioView JNI refenreces to windows. var views = make(map[C.jlong]*window) +// windows maps from Callbacks to windows +var windows = make(map[Callbacks]*window) + var mainWindow = newWindowRendezvous() var mainFuncs = make(chan func(env *C.JNIEnv), 1) @@ -209,15 +222,20 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j m.setCursor = getMethodID(env, class, "setCursor", "(I)V") }) view = C.gio_jni_NewGlobalRef(env, view) - w := &window{ - view: view, - } wopts := <-mainWindow.out - w.callbacks = wopts.window + w, ok := windows[wopts.window] + if !ok { + w = &window{ + callbacks: wopts.window, + } + windows[wopts.window] = w + } w.callbacks.SetDriver(w) + w.view = view handle := C.jlong(view) views[handle] = w w.loadConfig(env, class) + applyStateDiff(env, view, windowState{}, w.state) w.setStage(system.StagePaused) w.callbacks.Event(ViewEvent{View: uintptr(view)}) return handle @@ -659,6 +677,33 @@ func (w *window) ReadClipboard() { } func (w *window) SetCursor(name pointer.CursorName) { + w.setState(func(state *windowState) { + state.cursor = &name + }) +} + +// setState adjust the window state on the main thread. +func (w *window) setState(f func(state *windowState)) { + runOnMain(func(env *C.JNIEnv) { + f(&w.newState) + if w.view == 0 { + // No View attached. The state will be applied at next onCreateView. + return + } + old := w.state + state := w.newState + applyStateDiff(env, w.view, old, state) + w.state = state + }) +} + +func applyStateDiff(env *C.JNIEnv, view C.jobject, old, state windowState) { + if state.cursor != nil && old.cursor != state.cursor { + setCursor(env, view, *state.cursor) + } +} + +func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) { var curID int switch name { default: @@ -678,9 +723,7 @@ func (w *window) SetCursor(name pointer.CursorName) { case pointer.CursorNone: curID = 0 // TYPE_NULL } - runOnMain(func(env *C.JNIEnv) { - callVoidMethod(env, w.view, gioView.setCursor, jvalue(curID)) - }) + callVoidMethod(env, view, gioView.setCursor, jvalue(curID)) } // Close the window. Not implemented for Android.