2024/03/07
引き続き海外出張中。本業(サラリーマン)の仕事や交流が終わってからとなると、どうしてもアイディアレーンの開発時間は限られる。
そしてやっていることはこの前のFlutter Upgradeで起きたキーボードイベント仕様変更の対応の続き。未だに直りきらず。
今困っているのは下記の問題。
- Flutterバージョンアップに合わせてやむなくEnterキーの判定処理をRawKeyDownEvent から KeyDownEvent に変えた
- その結果、これまで拾っていなかった日本語入力中のEnterキーイベントまで意図せず拾ってしまうようになった
- 従来発生しなかったイベントが起きるようになり、Enterを押したときの処理が色々おかしくなった
そういえばTextFieldで編集中のEnterキーのイベント処理はもともと訳が分からず、昔下記のように挙動をまとめていたのだった。それを忘れていた。
// 【TextFieldで編集中の、Enterキーのイベント処理について】
//・テキスト編集時のキー動作のoverrideについて 「Text Editing Actions」 https://zenn.dev/inari_sushio/articles/a65da631a5c6da
//
// ■Webでの処理順
// 日本語入力無しでEnter
// (1) IdeaState.onEditingComplete
// (2) IdeaState.onSubmitted
// (3) RawKeyUpEvent.Enter (DownEventは取れるときと取れない時がある)
// 日本語入力中のEnter
// (1) RawKeyDownEvent.Enter
// ■PCでの処理順
// 日本語入力無しでEnter
// (1) RawKeyDownEvent.Enter
// (2) IdeaState.onEditingComplete
// (3) IdeaState.onSubmitted
// (4) RawKeyUpEvent.Enter
// 日本語入力中のEnter
// 何もイベント発生せず
//
//・【TextFieldでEnterを押した場合のキーの処理の違いについて】
// ・webだと、submit→key webの順で処理される
// ・windowsだと、key → submitの順で処理される
WebとPCで処理が全然違うし本当にややこしい。。。
今回RawKeyEventからKeyEventに変更したことにより、またこれとも挙動が違う状況になったのかもしれない。。。
Flutterの日本語制御は正直本当に不便だ。
ぼやいても仕方が無い。
とにかく日本語入力中(IMEが漢字変換モード中)に押されたEnterかそうでないかを判定しなければならない。
ただ下記を含めて色々とやり方を探したが、良い情報が見つからない。
SafariでIME確定時のEnterを上手く制御できなかった話
もしかしたらRawKeyboardListerに戻さないと判定は難しいかもしれない。
なんとブラウザとIMEの組み合わせによっても挙動が違うようだ。
jQueryでIME入力確定時にイベントを発行する
結局ネットをさんざん探したものの、FlutterでEnterキーを押されたときに日本語入力中(IMEが漢字変換モード中)に押されたEnterかそうでないかを正しく見分ける術を見つけられなかった。
仕方なく自分で色々試したところ、通常はEnterのキーコードとしては13が返ってくるが、“Webの”IME入力中のEnterだけは13ではなく229が返ってくることが分かった。
ただ結局これを使った判定は、やりたかった処理の流れではうまくいかないことが分かった。
海外出張中に時間を見つけて取り組んだというのに、機能追加どころか何も進まず。
2024/03/08
帰りの飛行機の中で開発。不具合対応や一部リファクタを行った。
「Enterが押された時に日本語入力中かどうかを判定する問題」がどうしても解決出来ないのでStackOverFlowに投稿することにした。海外からの帰りの飛行機で作文。のんびりすれば良いのに我ながら何をやっているのだか。
それにしても「IMEで日本語入力中」ということを海外の開発者に英語でなんと伝えれば良いのか?色々調べた結果「Composingモードがそうでないか」という言い方で伝わりそうだ。
下記はStackOverFlow投稿に向けた下書き
FlutterのTextFieldで、Composing中に押されたEnterキーか、そうでない時のEnterキーかを判別したい。
私はTextFieldでの入力中にEnterキーが押されたタイミングで処理を行いたい。しかしComposing中かそうで無いかを区別出来ずに困っています。
TextFieldをFocus()でラップしonKeyEventで判別することでEnterキーが押されたことが分かりますが、Composing中かどうかは分かりません。 実際、IMEがCompose状態でEnterが押された時にもonKeyEventは起きます。
しかしEnterが押されてComposeモードが終わった直後にonKeyEventに入るため、Compose中に押されたのかそうで無い時に押されたのか判別出来ません。(Compose中に押されたEnterなら一部の処理をスキップするなどをやりたい)
私はEnterキーとShiftやControlの組み合わせを検知したいので、onSubmittedでは目的を果たせないと考えています。何か良い方法はあるでしょうか?
—
[Flutter] How to detect whether the Enter key was pressed during Composing or Not in TextField.
I want to handle my logic when the Enter key is pressed while inputting in a TextField. However, I am having trouble distinguishing whether composing was in progress or not when Enter key was pressed.
By wrapping the TextField with Focus() and using onKeyEvent, I can detect that the Enter key has been pressed. But I cannot know whether composing was in progress or not when Enter key was pressed.
In fact, onKeyEvent occurs even when Enter is pressed while the IME is in Compose state. However, because onKeyEvent is entered immediately after Enter is pressed and Compose mode ends, it is not possible to determine whether it was pressed during Compose or outside of Compose. (I want to skip some processing if Enter is pressed during Compose.)
I want to detect combinations of Enter and Shift or Control, so I don’t think onSubmitted will serve my purpose. Is there a better way?
とここまで書いたものの非常にややこしい。
英文を見ても伝わりづらい感じがする。
もっとシンプルな構成で動作検証して、論点を整理してから投稿したいと思う。明日に持ち越し。
2024/03/09
引き続き日本語入力時のEnterキーを判定する問題。
日本語入力中にEnterを押した場合(いわゆる確定のEnterの時)にどのようにイベントが返ってくるかをシンプルなプログラムで試した。
PC(Mac)だとKeyDownEventとKeyUpEventが走る。
KeyDownEventの時composingはオン、KeyUpEventの時composingはオフ。
次にブラウザ(Windows)だとKeyDownEventだけが走る。KeyDownEventの時composingはオフ。
テストに使ったソース
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var controller = TextEditingController();
@override
void initState() {
controller.addListener(_onTextControllerChanged);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Focus(
onKeyEvent: _detectKeyEvent,
child: TextField(
controller: controller,
),
),
),
);
}
KeyEventResult _detectKeyEvent(FocusNode node, KeyEvent event) {
if (event.logicalKey.keyLabel == 'Enter') {
print("KeyDownEvent:${event is KeyDownEvent}");
// How to detect that Enter was hit at composing state or not?
print("value.composing.isValid:${controller.value.composing.isValid}");
print("value.composing.isCollapsed:${controller.value.composing.isCollapsed}");
}
return KeyEventResult.ignored;
}
void _onTextControllerChanged() {
// print("value.composing.isValid:${controller.value.composing.isValid}");
// print("value.composing.isCollapsed:${controller.value.composing.isCollapsed}");
// if (controller.value.isComposingRangeValid) {
// print("value.composing.textInside(value.text):${controller.value.composing.textInside(controller.value.text)}");
// } else {
// print("value.composing.isComposingRangeValid:false");
// }
}
}
ちなみにスクリーンショットは無いが、Compose中で無い場合(日本語入力OFFの時)のEnterキーは、どちらもKeyDownだけが走った。
まとめると
Compose状態(日本語入力中)にTextFieldでEnterキーを押したときの挙動:
- Macの場合 KeyDownEvent(Composing ON) → KeyUpEvent(Composing OFF)
- WindowsのChromeの場合 KeyDownEvent(Composing OFF)のみ
はちゃめちゃだ。
ただ上記の法則からすると、IME OFF時のEnterキーだけ処理を行いたいなら KeyUpは無視してKeyDownのみで処理を行い、かつComposing中は無視をする、でなんとかなりそうか。
ただこれがChrome以外のブラウザでも同じ挙動なのか、そして別のIMEを使っても同じ挙動なのかなど色々心配ではある。ただ調べるにしても大変な労力がかかりそうだ。
→その後WindowsのChromeでもMacと同じような処理になる場合もあり、どうやらIMEによっても違うようだった。。。訳が分からない。
とにかくやりたかったことは「TextFieldでEnterキーが押されたら処理を行いたいが、日本語入力中のEnterキーは拾わないようにしたい」ことだった。
ここまでの調査の結果「Windows/MacともChrome前提であれば”KeyUpを無視してKeyDownだけでEnterを取得し、かつ textController.value.composing.isValid が true”なら日本語入力中であるとみなし無視する」という処理でなんとかなりそうだ。(長い)
//ソースのイメージ
return Focus(
onKeyEvent: _processKeysAtEditingIdea,
child: textField,
);
//~
KeyEventResult _processKeysAtEditingIdea(FocusNode node, KeyEvent keyEvent) {
if (keyEvent.logicalKey.keyLabel == 'Enter' && keyEvent is KeyDownEvent) {
//WebではComposing中でもキーを捉えてしまうので、その場合は後続処理を行わないでリターン
if (textController.value.composing.isValid) {
return KeyEventResult.ignored;
}
//... 以降にやりたい処理を書く
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
}
数日かかったがなんとか目処がついたのは良かった。
こんな感じでFlutterのバージョンアップ(Upgrade)の後始末のせいで既に何日もスタックしている。
なかなか機能が作れない……。
※開発日記は当時の記録をもとに作成し、必要に応じて加筆・補足しています
この記事はアイディア整理ソフト「idea Lane」の開発記録です
↓どなたでも、ユーザ登録だけで無料ですぐに使えます↓
テキストベースの思考整理ツール「idea Lane」
コメント