From 11bdd3cfcd96df7d64af64443267a3dcab9a59b3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:24:57 +0800 Subject: [PATCH] fix: ios keyboard, composing input (#8471) * fix: ios keyboard, composing input Signed-off-by: fufesou * Incorrect changes Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 93 ++++++++++++++++------- 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index b47c6b132..c86df2242 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -55,6 +55,9 @@ class _RemotePageState extends State { InputModel get inputModel => gFFI.inputModel; SessionID get sessionId => gFFI.sessionId; + final TextEditingController _textController = + TextEditingController(text: initText); + @override void initState() { super.initState(); @@ -145,37 +148,59 @@ class _RemotePageState extends State { setState(() {}); } - // handle mobile virtual keyboard - void handleSoftKeyboardInput(String newValue) { + void _handleIOSSoftKeyboardInput(String newValue) { var oldValue = _value; _value = newValue; - if (isIOS) { - var i = newValue.length - 1; - for (; i >= 0 && newValue[i] != '\1'; --i) {} - var j = oldValue.length - 1; - for (; j >= 0 && oldValue[j] != '\1'; --j) {} - if (i < j) j = i; - newValue = newValue.substring(j + 1); - oldValue = oldValue.substring(j + 1); - var common = 0; - for (; - common < oldValue.length && - common < newValue.length && - newValue[common] == oldValue[common]; - ++common) {} - for (i = 0; i < oldValue.length - common; ++i) { - inputModel.inputKey('VK_BACK'); - } - if (newValue.length > common) { - var s = newValue.substring(common); - if (s.length > 1) { - bind.sessionInputString(sessionId: sessionId, value: s); - } else { - inputChar(s); - } - } - return; + var i = newValue.length - 1; + for (; i >= 0 && newValue[i] != '\1'; --i) {} + var j = oldValue.length - 1; + for (; j >= 0 && oldValue[j] != '\1'; --j) {} + if (i < j) j = i; + var subNewValue = newValue.substring(j + 1); + var subOldValue = oldValue.substring(j + 1); + + // get common prefix of subNewValue and subOldValue + var common = 0; + for (; + common < subOldValue.length && + common < subNewValue.length && + subNewValue[common] == subOldValue[common]; + ++common) {} + + // get newStr from subNewValue + var newStr = ""; + if (subNewValue.length > common) { + newStr = subNewValue.substring(common); } + + // Set the value to the old value and early return if is still composing. (1 && 2) + // 1. The composing range is valid + // 2. The new string is shorter than the composing range. + if (_textController.value.isComposingRangeValid) { + final composingLength = _textController.value.composing.end - + _textController.value.composing.start; + if (composingLength > newStr.length) { + _value = oldValue; + return; + } + } + + // Delete the different part in the old value. + for (i = 0; i < subOldValue.length - common; ++i) { + inputModel.inputKey('VK_BACK'); + } + + // Input the new string. + if (newStr.length > 1) { + bind.sessionInputString(sessionId: sessionId, value: newStr); + } else { + inputChar(newStr); + } + } + + void _handleNonIOSSoftKeyboardInput(String newValue) { + var oldValue = _value; + _value = newValue; if (oldValue.isNotEmpty && newValue.isNotEmpty && oldValue[0] == '\1' && @@ -214,6 +239,15 @@ class _RemotePageState extends State { } } + // handle mobile virtual keyboard + void handleSoftKeyboardInput(String newValue) { + if (isIOS) { + _handleIOSSoftKeyboardInput(newValue); + } else { + _handleNonIOSSoftKeyboardInput(newValue); + } + } + void inputChar(String char) { if (char == '\n') { char = 'VK_RETURN'; @@ -227,6 +261,7 @@ class _RemotePageState extends State { gFFI.invokeMethod("enable_soft_keyboard", true); // destroy first, so that our _value trick can work _value = initText; + _textController.text = _value; setState(() => _showEdit = false); _timer?.cancel(); _timer = Timer(kMobileDelaySoftKeyboard, () { @@ -491,7 +526,7 @@ class _RemotePageState extends State { autofocus: true, focusNode: _mobileFocusNode, maxLines: null, - initialValue: _value, + controller: _textController, // trick way to make backspace work always keyboardType: TextInputType.multiline, onChanged: handleSoftKeyboardInput,