Withdraw textPropertyForNode API, add removeKeyboardTypeForNode API#442
Withdraw textPropertyForNode API, add removeKeyboardTypeForNode API#442jperedadnr merged 6 commits intogluonhq:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR removes the previously added “composed text tracking” API (textPropertyForNode) and its native plumbing, and reworks keyboard-type handling to be focus-driven while adding APIs to unregister keyboard-type and visibility tracking.
Changes:
- Withdraw
textPropertyForNodeand remove Android composing-text callback/config/native stubs. - Add
removeKeyboardTypeForNode(Node)andreleaseVisibilityForNode(Node)APIs. - Switch keyboard type application from per-node mouse event filtering to scene focus-owner tracking to reduce race conditions.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/keyboard/src/main/resources/META-INF/substrate/config/jniconfig-aarch64-android.json | Removes JNI config entry for composing-text callback. |
| modules/keyboard/src/main/native/android/c/keyboard.c | Removes composing-text JNI method lookup and native-to-Java forwarding function. |
| modules/keyboard/src/main/java/com/gluonhq/attach/keyboard/impl/IOSKeyboardService.java | Drops overrides tied to removed APIs; relies on Base implementation. |
| modules/keyboard/src/main/java/com/gluonhq/attach/keyboard/impl/BaseKeyboardService.java | Adds unregister APIs, moves visibility tracking into Base, and adds scene focus tracking to drive keyboard type. |
| modules/keyboard/src/main/java/com/gluonhq/attach/keyboard/impl/AndroidKeyboardService.java | Removes composing-text callback and redundant visibility methods. |
| modules/keyboard/src/main/java/com/gluonhq/attach/keyboard/KeyboardService.java | Removes textPropertyForNode, adds releaseVisibilityForNode and removeKeyboardTypeForNode. |
| gradle/include/android/grandroid_ext.h | Removes composing-text hook declaration/stub. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| BaseKeyboardService() { | ||
| VISIBLE_HEIGHT.addListener((obs, ov, nv) -> { | ||
| if (nv != null && nv.doubleValue() <= 0) { | ||
| if (debug) { | ||
| LOG.info("Keyboard hidden, reset default type"); | ||
| } | ||
| applyActiveNodeId(""); // reset active node | ||
| applyKeyboardType(KeyboardType.ASCII.getValue()); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
The previous implementation reset the active node id and keyboard type back to ASCII when VISIBLE_HEIGHT dropped to 0 (keyboard hidden). With the constructor now empty, that reset no longer happens, which changes behavior and may conflict with the setKeyboardTypeForNode contract/Javadoc. If the reset is still intended, reintroduce it (possibly with the updated focus-based approach); otherwise consider updating the public Javadoc to match the new behavior.
| * Removes the keyboard type assignment and event filter previously installed via | ||
| * {@link #setKeyboardTypeForNode(Node, KeyboardType)}. After this call the node | ||
| * will simply use the default keyboard type. |
There was a problem hiding this comment.
The Javadoc says this removes an “event filter”, but the implementation now uses a scene-level focusOwnerProperty() listener rather than an event filter on the node. Please update the documentation to describe what is actually installed/removed (and what the lifecycle expectations are).
| * Removes the keyboard type assignment and event filter previously installed via | |
| * {@link #setKeyboardTypeForNode(Node, KeyboardType)}. After this call the node | |
| * will simply use the default keyboard type. | |
| * Removes the keyboard type assignment previously registered via | |
| * {@link #setKeyboardTypeForNode(Node, KeyboardType)} and stops tracking scene | |
| * focus-owner changes for that node. The registration remains active until this | |
| * method is called. After this call the node will simply use the default keyboard | |
| * type. |
| ATTACH_LOG_FINE("Initializing native Keyboard from OnLoad"); | ||
| jAttachKeyboardClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/gluonhq/attach/keyboard/impl/AndroidKeyboardService")); | ||
| jAttach_notifyHeightMethod = (*env)->GetStaticMethodID(env, jAttachKeyboardClass, "notifyVisibleHeight", "(F)V"); | ||
| jAttach_notifyComposingTextMethod = (*env)->GetStaticMethodID(env, jAttachKeyboardClass, "notifyComposingText", "(Ljava/lang/String;Ljava/lang/String;)V"); | ||
| initKeyboard(); |
There was a problem hiding this comment.
notifyComposingText support was removed, but jAttach_notifyComposingTextMethod is still declared at the top of the file. With no initialization or use, this can trigger unused-variable warnings in stricter builds. Please remove the leftover jAttach_notifyComposingTextMethod declaration (and any related includes/defs if present).
| private void attachFocusTracker(Node node) { | ||
| Scene scene = node.getScene(); | ||
| if (scene != null) { | ||
| trackScene(scene); | ||
| // If this node is already the focus owner, apply its type now | ||
| if (scene.getFocusOwner() == node) { | ||
| applyTypeFor(node); | ||
| } | ||
| return; |
There was a problem hiding this comment.
When tracking a Scene for the first time, the current focus owner’s keyboard type/id isn’t applied unless it happens to be the node being registered. Since focusOwnerProperty() listeners don’t fire on registration, the native layer can remain out of sync until the next focus change. Consider applying applyTypeFor(scene.getFocusOwner()) right after installing the listener (and similarly after trackScene(nv) in the deferred branch).
| idToNode.put(syntheticId(n), n); | ||
| return new ReadOnlyStringWrapper(""); | ||
| }).getReadOnlyProperty(); | ||
| nodeKeyboardTypes.remove(node); |
There was a problem hiding this comment.
removeKeyboardTypeForNode only removes the stored mapping. If the node is currently the focus owner, the native keyboard type won’t revert to the default until focus changes again. Consider detecting this case (e.g., node.getScene()!=null && node.getScene().getFocusOwner()==node) and calling the same apply logic so the default ASCII type/id is pushed immediately.
| nodeKeyboardTypes.remove(node); | |
| nodeKeyboardTypes.remove(node); | |
| Scene scene = node.getScene(); | |
| if (scene != null && scene.getFocusOwner() == node) { | |
| applyTypeFor(node); | |
| } |
| /** Map of nodes to their visibility listeners. */ | ||
| private final Map<Node, ChangeListener<Number>> visibilityListeners = new WeakHashMap<>(); | ||
|
|
There was a problem hiding this comment.
visibilityListeners is a WeakHashMap, but each stored listener lambda closes over the node key, which defeats the weak-key behavior and can keep nodes alive until releaseVisibilityForNode is called. Consider using a regular HashMap (and documenting the need to release), or refactor the listener to hold only weak references to the node/parent if you want automatic cleanup.
This PR is a follow-up of #439