mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-06-07 09:53:42 -04:00
[Android] Fix swkbd inline keyboard initial text sync and cursor position
When the game opens an inline keyboard with pre-filled text (e.g. a previously saved Mii name), the text was stored in m_current_text on the C++ side but never communicated to the Android IME. As a result, the Android keyboard field appeared empty, backspace did nothing, and typing replaced the existing text instead of appending to it. The root cause: the game sends InlineTextChanged() with the pre-filled text in the same Calc request as appear(), before ShowInlineKeyboard() is called. m_current_text was not updated by InlineTextChanged, and ShowInlineKeyboard was using the stale parameters.initial_text (set during initialization before the text was known). Fix on the C++ side: - Initialize m_current_text from parameters.initial_text in InitializeKeyboard so each session starts clean. - Update m_current_text in InlineTextChanged so the latest game text is always reflected. - Pass m_current_text (not parameters.initial_text) as initial_text to Kotlin when calling executeInline. Fix on the Kotlin side: - Pre-populate imeEditable with the received initial text. - Call Selection.setSelection to place the cursor at the end so that backspace and new input work correctly from the start.
This commit is contained in:
parent
0cc0db0a65
commit
c0804a4e86
4 changed files with 25 additions and 5 deletions
|
|
@ -20,6 +20,7 @@ import java.io.Serializable
|
|||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
|
||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||
|
||||
@Keep
|
||||
object SoftwareKeyboard {
|
||||
|
|
@ -37,6 +38,7 @@ object SoftwareKeyboard {
|
|||
val emulationActivity = NativeLibrary.sEmulationActivity.get()
|
||||
|
||||
val overlayView = emulationActivity!!.findViewById<View>(R.id.surface_input_overlay)
|
||||
(overlayView as? InputOverlay)?.resetImeBuffer(config.initial_text ?: "")
|
||||
overlayView.requestFocus()
|
||||
val im =
|
||||
overlayView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.Selection
|
||||
import android.util.AttributeSet
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.KeyEvent
|
||||
|
|
@ -57,6 +58,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
|||
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
|
||||
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
|
||||
private val imeEditable = Editable.Factory.getInstance().newEditable("")
|
||||
private var pendingInitialText: String = ""
|
||||
|
||||
private var inEditMode = false
|
||||
private var gamelessMode = false
|
||||
|
|
@ -85,15 +87,24 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
|
|||
|
||||
override fun onCheckIsTextEditor(): Boolean = true
|
||||
|
||||
fun resetImeBuffer(initialText: String = "") {
|
||||
pendingInitialText = initialText
|
||||
}
|
||||
|
||||
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
|
||||
imeEditable.clear()
|
||||
if (pendingInitialText.isNotEmpty()) {
|
||||
imeEditable.append(pendingInitialText)
|
||||
}
|
||||
pendingInitialText = ""
|
||||
Selection.setSelection(imeEditable, imeEditable.length)
|
||||
outAttrs.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or
|
||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or
|
||||
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI or EditorInfo.IME_ACTION_DONE
|
||||
outAttrs.initialSelStart = 0
|
||||
outAttrs.initialSelEnd = 0
|
||||
outAttrs.initialSelStart = imeEditable.length
|
||||
outAttrs.initialSelEnd = imeEditable.length
|
||||
|
||||
return object : BaseInputConnection(this, true) {
|
||||
override fun getEditable(): Editable = imeEditable
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ void AndroidKeyboard::InitializeKeyboard(
|
|||
}
|
||||
|
||||
parameters = std::move(initialize_parameters);
|
||||
m_current_text = parameters.initial_text;
|
||||
|
||||
LOG_INFO(Frontend,
|
||||
"\nKeyboardInitializeParameters:"
|
||||
|
|
@ -185,9 +186,13 @@ void AndroidKeyboard::ShowInlineKeyboard(
|
|||
|
||||
// Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
|
||||
m_is_inline_active = true;
|
||||
std::thread([&] {
|
||||
// Pass m_current_text as initial_text so Kotlin receives any text set via InlineTextChanged
|
||||
// before this call (e.g. game pre-fills the field in the same Calc request as appear).
|
||||
std::thread([&, current_text = m_current_text] {
|
||||
Core::Frontend::KeyboardInitializeParameters p = parameters;
|
||||
p.initial_text = current_text;
|
||||
GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
|
||||
ToJKeyboardParams(parameters));
|
||||
ToJKeyboardParams(p));
|
||||
}).join();
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +212,8 @@ void AndroidKeyboard::InlineTextChanged(
|
|||
"\ncursor_position={}",
|
||||
Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
|
||||
|
||||
m_current_text = text_parameters.input_text;
|
||||
|
||||
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
|
||||
text_parameters.input_text, text_parameters.cursor_position);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ private:
|
|||
|
||||
private:
|
||||
mutable bool m_is_inline_active{};
|
||||
std::u16string m_current_text;
|
||||
mutable std::u16string m_current_text;
|
||||
};
|
||||
|
||||
// Should be called in JNI_Load
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue