Skip to content

Commit 53b9b3f

Browse files
authored
add a workaround for TextField delete on macOS. (#535)
1 parent 933a49f commit 53b9b3f

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

lib/app.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import 'utils/extension/extension.dart';
3030
import 'utils/hook.dart';
3131
import 'utils/logger.dart';
3232
import 'utils/system/system_fonts.dart';
33+
import 'utils/system/text_input.dart';
3334
import 'utils/system/tray.dart';
3435
import 'widgets/brightness_observer.dart';
3536
import 'widgets/focus_helper.dart';
@@ -219,7 +220,11 @@ class _App extends StatelessWidget {
219220
? textScaleFactor
220221
: mediaQueryData.textScaleFactor,
221222
),
222-
child: SystemTrayWidget(child: child!),
223+
child: SystemTrayWidget(
224+
child: TextInputActionHandler(
225+
child: child!,
226+
),
227+
),
223228
),
224229
);
225230
},

lib/utils/system/text_input.dart

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import 'dart:io';
2+
3+
import 'package:flutter/widgets.dart';
4+
5+
import '../logger.dart';
6+
7+
// remove this once https://github.com/flutter/flutter/issues/78061 is fixed.
8+
class TextInputActionHandler extends StatefulWidget {
9+
const TextInputActionHandler({
10+
Key? key,
11+
required this.child,
12+
}) : super(key: key);
13+
14+
final Widget child;
15+
16+
@override
17+
State<TextInputActionHandler> createState() => _TextInputActionHandlerState();
18+
}
19+
20+
class _TextInputActionHandlerState extends State<TextInputActionHandler> {
21+
late final _actions = <Type, Action<Intent>>{
22+
DeleteCharacterIntent: makeAction<DeleteCharacterIntent>(context),
23+
ExtendSelectionByCharacterIntent:
24+
makeAction<ExtendSelectionByCharacterIntent>(context),
25+
ExtendSelectionVerticallyToAdjacentLineIntent:
26+
makeAction<ExtendSelectionVerticallyToAdjacentLineIntent>(context),
27+
SelectAllTextIntent: makeAction<SelectAllTextIntent>(context),
28+
PasteTextIntent: makeAction<PasteTextIntent>(context),
29+
RedoTextIntent: makeAction<RedoTextIntent>(context),
30+
UndoTextIntent: makeAction<UndoTextIntent>(context),
31+
};
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
if (!Platform.isMacOS) {
36+
return widget.child;
37+
}
38+
return Actions(
39+
actions: _actions,
40+
child: widget.child,
41+
);
42+
}
43+
}
44+
45+
Action<T> makeAction<T extends Intent>(BuildContext context) =>
46+
Action<T>.overridable(
47+
defaultAction: _CallbackContextAction(),
48+
context: context,
49+
);
50+
51+
class _CallbackContextAction<T extends Intent> extends ContextAction<T> {
52+
_CallbackContextAction();
53+
54+
bool? _consumeKey;
55+
56+
@override
57+
bool consumesKey(T intent) {
58+
final consumeKey = _consumeKey;
59+
_consumeKey = null;
60+
if (consumeKey != null) {
61+
return consumeKey;
62+
}
63+
return callingAction?.consumesKey(intent) ?? true;
64+
}
65+
66+
@override
67+
Object? invoke(T intent, [BuildContext? context]) {
68+
if (context == null) {
69+
e('No context provided to _CallbackContextAction');
70+
return callingAction?.invoke(intent);
71+
}
72+
final state = context.findAncestorStateOfType<EditableTextState>();
73+
if (state == null) {
74+
e('failed to find EditableTextState');
75+
return callingAction?.invoke(intent);
76+
}
77+
78+
final composingRange = state.textEditingValue.composing;
79+
80+
if (composingRange.isValid && !composingRange.isCollapsed) {
81+
_consumeKey = false;
82+
return null;
83+
}
84+
return callingAction?.invoke(intent);
85+
}
86+
}

0 commit comments

Comments
 (0)