-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Input method (soft keyboard) support #24
Conversation
5a91699
to
513dc7d
Compare
Trying to test this, it doesn't currently seem to be working as intended. I tried modifying agdk-mainloop so that it handles touches to the top-left of the screen by showing (toggling) the onscreen keyboard and printing input method state updates. I see an input method 'commit' for every key press for some reason and the attempt to initialize the text state with some "Lorem ipsum" text and a selection didn't seem to work. Once the keyboard is visible then other input handling seems to somehow fall apart, like touch events are being grabbed. I may just look at landing the APIs for showing/hiding the keyboard first and a change that ensures that |
I ended up making incremental PRs for the show/hide_soft_input APIs and |
android-activity/src/input.rs
Outdated
pub start: Option<usize>, | ||
|
||
/// The end of the span (exclusive) | ||
pub end: Option<usize>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During my testing, leaving end None and providing only start, caused a android exception, so I think having start and end just be usize and then Option in TextInputState might be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I originally based this on these docs:
/**
* This struct holds a span within a region of text from start (inclusive) to
* end (exclusive). An empty span or cursor position is specified with
* start==end. An undefined span is specified with start = end = SPAN_UNDEFINED.
*/
typedef struct GameTextInputSpan {
/** The start of the region (inclusive). */
int32_t start;
/** The end of the region (exclusive). */
int32_t end;
} GameTextInputSpan;
I think this issue is exactly what I got stuck on last time I was investigating this but didn't fully dig into the details.
Following the flow of data here...
-
This data gets passed to
GameActivity_setTextInputState
withNone
mapped to-1
based on the above comment. -
GameActivity_setTextInputState
passes this toGameTextInput_setState
via aCMD_SET_SOFT_INPUT_STATE
callback.
GameTextInput_setState
calld GameTextInput::setState
which does a JNI call to gametextinput.InputConnection.setState
in Java (https://android.googlesource.com/platform/frameworks/opt/gamesdk/+/refs/heads/main/game-text-input/src/main/java/com/google/androidgamesdk/gametextinput/InputConnection.java#196)
setState
is a utility that then effectively calls InputConnection.setSelection
and InputConnection.setComposingRegion
which are standard APIs documented here: https://developer.android.com/reference/android/view/inputmethod/InputConnection#setComposingRegion(int,%20int)
It looks like InputConnection
is expected to clamp indices to being in-bounds and also not be fussy about wither start comes after end. At least the docs for setComposingRegions
say "The passed indices are clipped to the contents bounds" which suggests it should be ok to pass None/-1
for the start/end indices.
Maybe the java GameTextInput code isn't validating its inputs properly.
It look like it should also be considering indices that are out of bounds because they are negative.
Or maybe only setComposingRegions
is designed to clamp/clip the indices and you're expected to keep them in-bounds for setSelection
.
Conceptually it looks like there is more of a special case for setComposingRegion
which specifically recognises a start index of -1
and will map that to removeComposingRegion
.
Maybe the comments that were written for GameTextInputSpan
about using SPAN_UNDEFINED
only really apply for the composing region.
That would maybe make sense considering that the selection becomes a cursor if the start/end are the same but you can't completely remove the selection and the cursor too.
As you suggested - moving the Options up a level probably makes sense.
I'm currently think that maybe only the compose_region
should be optional though.
For the selection then if it was set to None
then we'd have to arbitrarily choose to either put the cursor at the start of end I guess but it seems like that should be explicitly decided by the app probably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My updated egui code looks a bit cleaner now, so I think this makes sense. I'm not 100% sure though on whether selection should be optional or not.
Okey, I've rebased this on GameActivity 2.0.2 and I've made it so TextSpan just has @lucasmerlin it'd be great if you're able to test this based on your Winit / egui changes. |
android-activity/src/input.rs
Outdated
pub start: Option<usize>, | ||
|
||
/// The end of the span (exclusive) | ||
pub end: Option<usize>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My updated egui code looks a bit cleaner now, so I think this makes sense. I'm not 100% sure though on whether selection should be optional or not.
Yeah I'm not 100% sure about the handling of the optional spans. Looking at the docs for
so from that pov it could also be reasonable that the Tbh it seems odd to me that the GameActivity implementation of InputConnection has this special case which isn't documented in the context of Passing None for the composing_region will result in Considering that it's really just a placeholder editable it doesn't matter much I expect that this will remove the span. I'm fairly sure that the Java implementation should also remove the span in case an empty region is specified, considering the SDK docs for
So actually maybe the right thing to do in android-activity is to recognise zero-sized composing regions ourselves and then normalize that into a This would mean we can remove the |
Although it could make sense for the composing_region to be non-optional when setting the region, it would be awkward when querying the state because it needs to be optional when reading the state since the start/end will be -1 if no region has been set yet or if it's is cleared. It would probably still be best to represent an unset composing region with |
oh, oops, sorry I just re-requested review @lucasmerlin but I forgot I was going to change it so we check for an empty compose region in Rust and normalize that as -1,-1 - will do that now |
This also adds `InputEvent::TextEvent` for notifying applications of IME state changes as well as explicit getter/setter APIs for tracking IME selection + compose region state. (only supported with GameActivity) Fixes: #18
okey, done I'm hoping that if you're perhaps able to smoke test this with your winit/egui branch @lucasmerlin then we can perhaps land this so we can potentially look at getting the corresponding winit changes into 0.29 if lucky |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works perfectly now!
Only missing thing now is some way to set the keyboard flags, but I think that can be done in a separate PR.
I currently use these for Autocorrect + Sentence capitalization:
ffi::GameActivity_setImeEditorInfo(activity, 1 | 16384, 0, 0);
Cool, thanks for checking, and yeah we can look at adding API for keyboard flags via a follow up PR |
Add input method support based on the GameTextInput library (only supported with
game-activity
currently)This is based on discussion for #18 and here