mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-04 05:47:26 +00:00
Add support for InputEvent::TextAction events
This exposes IME actions via an InputEvent::TextAction event so that it's possible to recognise when text entry via an input method is finished. This adds a `TextInputAction` enum to represent the action key on a soft keyboard, such as "Done". For example, this makes it possible to emit Ime::Commit events in Winit.
This commit is contained in:
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- input: TextInputAction enum representing action button types on soft keyboards.
|
||||
- input: InputEvent::TextAction event for handling action button presses from soft keyboards.
|
||||
- The `ndk` and `ndk-sys` crates are now re-exported under `android_activity::ndk` and `android_activity::ndk_sys` ([#194](https://github.com/rust-mobile/android-activity/pull/194))
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -27,6 +27,7 @@ pub enum InputEvent<'a> {
|
||||
MotionEvent(MotionEvent<'a>),
|
||||
KeyEvent(KeyEvent<'a>),
|
||||
TextEvent(crate::input::TextInputState),
|
||||
TextAction(crate::input::TextInputAction),
|
||||
}
|
||||
|
||||
/// A motion event.
|
||||
|
||||
@@ -21,7 +21,7 @@ use ndk::configuration::Configuration;
|
||||
use ndk::native_window::NativeWindow;
|
||||
|
||||
use crate::error::InternalResult;
|
||||
use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding};
|
||||
use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding, TextInputAction};
|
||||
use crate::jni_utils::{self, CloneJavaVM};
|
||||
use crate::util::{abort_on_panic, forward_stdio_to_logcat, log_panic, try_get_path_from_ptr};
|
||||
use crate::{
|
||||
@@ -174,9 +174,6 @@ impl NativeAppGlue {
|
||||
};
|
||||
let out_ptr = &mut out_state as *mut TextInputState;
|
||||
|
||||
let app_ptr = self.as_ptr();
|
||||
(*app_ptr).textInputState = 0;
|
||||
|
||||
// NEON WARNING:
|
||||
//
|
||||
// It's not clearly documented but the GameActivity API over the
|
||||
@@ -204,6 +201,14 @@ impl NativeAppGlue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_text_input_state(&self) -> TextInputState {
|
||||
unsafe {
|
||||
let app_ptr = self.as_ptr();
|
||||
(*app_ptr).textInputState = 0;
|
||||
}
|
||||
self.text_input_state()
|
||||
}
|
||||
|
||||
// TODO: move into a trait
|
||||
pub fn set_text_input_state(&self, state: TextInputState) {
|
||||
unsafe {
|
||||
@@ -247,6 +252,18 @@ impl NativeAppGlue {
|
||||
ffi::GameActivity_setTextInputState(activity, &ffi_state as *const _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_pending_editor_action(&self) -> Option<i32> {
|
||||
unsafe {
|
||||
let app_ptr = self.as_ptr();
|
||||
if (*app_ptr).pendingEditorAction {
|
||||
(*app_ptr).pendingEditorAction = false;
|
||||
Some((*app_ptr).editorAction)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -804,7 +821,8 @@ impl<'a> From<Arc<InputReceiver>> for InputIteratorInner<'a> {
|
||||
_receiver: receiver,
|
||||
buffered,
|
||||
native_app,
|
||||
text_event_checked: false,
|
||||
ime_text_input_state_checked: false,
|
||||
ime_editor_action_checked: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -821,7 +839,8 @@ pub(crate) struct InputIteratorInner<'a> {
|
||||
|
||||
buffered: Option<BufferedEvents<'a>>,
|
||||
native_app: NativeAppGlue,
|
||||
text_event_checked: bool,
|
||||
ime_text_input_state_checked: bool,
|
||||
ime_editor_action_checked: bool,
|
||||
}
|
||||
|
||||
impl InputIteratorInner<'_> {
|
||||
@@ -841,8 +860,10 @@ impl InputIteratorInner<'_> {
|
||||
self.buffered = None;
|
||||
}
|
||||
|
||||
if !self.text_event_checked {
|
||||
self.text_event_checked = true;
|
||||
// We make sure any input state changes are sent before we check
|
||||
// for editor actions, so actions will apply to the latest state.
|
||||
if !self.ime_text_input_state_checked {
|
||||
self.ime_text_input_state_checked = true;
|
||||
unsafe {
|
||||
let app_ptr = self.native_app.as_ptr();
|
||||
|
||||
@@ -854,12 +875,21 @@ impl InputIteratorInner<'_> {
|
||||
// the compiler isn't reordering code so this gets flagged
|
||||
// before the java main thread really updates the state.
|
||||
if (*app_ptr).textInputState != 0 {
|
||||
let state = self.native_app.text_input_state(); // Will clear .textInputState
|
||||
let state = self.native_app.take_text_input_state(); // Will clear .textInputState
|
||||
let _ = callback(&InputEvent::TextEvent(state));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.ime_editor_action_checked {
|
||||
self.ime_editor_action_checked = true;
|
||||
if let Some(action) = self.native_app.take_pending_editor_action() {
|
||||
let _ = callback(&InputEvent::TextAction(TextInputAction::from(action)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,6 +907,33 @@ pub struct TextInputState {
|
||||
pub compose_region: Option<TextSpan>,
|
||||
}
|
||||
|
||||
// Represents the action button on a soft keyboard.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive, num_enum::IntoPrimitive)]
|
||||
#[non_exhaustive]
|
||||
#[repr(i32)]
|
||||
pub enum TextInputAction {
|
||||
/// Let receiver decide what logical action to perform
|
||||
Unspecified = 0,
|
||||
/// No action - receiver could instead interpret as an "enter" key that inserts a newline character
|
||||
None = 1,
|
||||
/// Navigate to the input location (such as a URL)
|
||||
Go = 2,
|
||||
/// Search based on the input text
|
||||
Search = 3,
|
||||
/// Send the input to the target
|
||||
Send = 4,
|
||||
/// Move to the next input field
|
||||
Next = 5,
|
||||
/// Indicate that input is done
|
||||
Done = 6,
|
||||
/// Move to the previous input field
|
||||
Previous = 7,
|
||||
|
||||
#[doc(hidden)]
|
||||
#[num_enum(catch_all)]
|
||||
__Unknown(i32),
|
||||
}
|
||||
|
||||
/// An exclusive, lending iterator for input events
|
||||
pub struct InputIterator<'a> {
|
||||
pub(crate) inner: crate::activity_impl::InputIteratorInner<'a>,
|
||||
|
||||
@@ -434,4 +434,5 @@ pub enum InputEvent<'a> {
|
||||
MotionEvent(self::MotionEvent<'a>),
|
||||
KeyEvent(self::KeyEvent<'a>),
|
||||
TextEvent(crate::input::TextInputState),
|
||||
TextAction(crate::input::TextInputAction),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user