refactor: Easier shift enter logic for text input

pull/1683/head
Krille 4 months ago
parent 8956f81e4e
commit dda45f783f
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652

@ -110,7 +110,7 @@ class ChatController extends State<ChatPageWithRoom>
final AutoScrollController scrollController = AutoScrollController(); final AutoScrollController scrollController = AutoScrollController();
FocusNode inputFocus = FocusNode(); late final FocusNode inputFocus;
StreamSubscription<html.Event>? onFocusSub; StreamSubscription<html.Event>? onFocusSub;
Timer? typingCoolDown; Timer? typingCoolDown;
@ -275,8 +275,26 @@ class ChatController extends State<ChatPageWithRoom>
); );
} }
KeyEventResult _shiftEnterKeyHandling(FocusNode node, KeyEvent evt) {
if (!HardwareKeyboard.instance.isShiftPressed &&
evt.logicalKey.keyLabel == 'Enter') {
if (evt is KeyDownEvent) {
send();
}
return KeyEventResult.handled;
} else {
return KeyEventResult.ignored;
}
}
@override @override
void initState() { void initState() {
inputFocus = FocusNode(
onKeyEvent: (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile)
? _shiftEnterKeyHandling
: null,
);
scrollController.addListener(_updateScrollController); scrollController.addListener(_updateScrollController);
inputFocus.addListener(_inputFocusListener); inputFocus.addListener(_inputFocusListener);

@ -5,12 +5,9 @@ import 'package:emojis/emoji.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:pasteboard/pasteboard.dart';
import 'package:slugify/slugify.dart'; import 'package:slugify/slugify.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/markdown_context_builder.dart'; import 'package:fluffychat/utils/markdown_context_builder.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:fluffychat/widgets/mxc_image.dart';
import '../../widgets/avatar.dart'; import '../../widgets/avatar.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
@ -397,125 +394,66 @@ class InputBar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile); return TypeAheadField<Map<String, String?>>(
return Shortcuts( direction: VerticalDirection.up,
shortcuts: !useShortCuts hideOnEmpty: true,
? {} hideOnLoading: true,
: { controller: controller,
LogicalKeySet(LogicalKeyboardKey.shift, LogicalKeyboardKey.enter): focusNode: focusNode,
NewLineIntent(), hideOnSelect: false,
LogicalKeySet(LogicalKeyboardKey.enter): SubmitLineIntent(), debounceDuration: const Duration(milliseconds: 50),
LogicalKeySet( // show suggestions after 50ms idle time (default is 300)
LogicalKeyboardKey.controlLeft, builder: (context, controller, focusNode) => TextField(
LogicalKeyboardKey.keyM, controller: controller,
): PasteLineIntent(), focusNode: focusNode,
}, contextMenuBuilder: (c, e) => markdownContextBuilder(c, e, controller),
child: Actions( contentInsertionConfiguration: ContentInsertionConfiguration(
actions: !useShortCuts onContentInserted: (KeyboardInsertedContent content) {
? {} final data = content.data;
: { if (data == null) return;
NewLineIntent: CallbackAction(
onInvoke: (i) {
final val = controller!.value;
final selection = val.selection.start;
final messageWithoutNewLine =
'${controller!.text.substring(0, val.selection.start)}\n${controller!.text.substring(val.selection.end)}';
controller!.value = TextEditingValue(
text: messageWithoutNewLine,
selection: TextSelection.fromPosition(
TextPosition(offset: selection + 1),
),
);
return null;
},
),
SubmitLineIntent: CallbackAction(
onInvoke: (i) {
onSubmitted!(controller!.text);
return null;
},
),
PasteLineIntent: CallbackAction(
onInvoke: (i) async {
final image = await Pasteboard.image;
if (image != null) {
onSubmitImage!(image);
return null;
}
return null;
},
),
},
child: TypeAheadField<Map<String, String?>>(
direction: VerticalDirection.up,
hideOnEmpty: true,
hideOnLoading: true,
controller: controller,
focusNode: focusNode,
hideOnSelect: false,
debounceDuration: const Duration(milliseconds: 50),
// show suggestions after 50ms idle time (default is 300)
builder: (context, controller, focusNode) => TextField(
controller: controller,
focusNode: focusNode,
contextMenuBuilder: (c, e) =>
markdownContextBuilder(c, e, controller),
contentInsertionConfiguration: ContentInsertionConfiguration(
onContentInserted: (KeyboardInsertedContent content) {
final data = content.data;
if (data == null) return;
final file = MatrixFile( final file = MatrixFile(
mimeType: content.mimeType, mimeType: content.mimeType,
bytes: data, bytes: data,
name: content.uri.split('/').last, name: content.uri.split('/').last,
); );
room.sendFileEvent( room.sendFileEvent(
file, file,
shrinkImageMaxDimension: 1600, shrinkImageMaxDimension: 1600,
); );
}, },
),
minLines: minLines,
maxLines: maxLines,
keyboardType: keyboardType!,
textInputAction: textInputAction,
autofocus: autofocus!,
inputFormatters: [
LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
],
onSubmitted: (text) {
// fix for library for now
// it sets the types for the callback incorrectly
onSubmitted!(text);
},
decoration: decoration!,
onChanged: (text) {
// fix for the library for now
// it sets the types for the callback incorrectly
onChanged!(text);
},
textCapitalization: TextCapitalization.sentences,
),
suggestionsCallback: getSuggestions,
itemBuilder: (c, s) =>
buildSuggestion(c, s, Matrix.of(context).client),
onSelected: (Map<String, String?> suggestion) =>
insertSuggestion(context, suggestion),
errorBuilder: (BuildContext context, Object? error) =>
const SizedBox.shrink(),
loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
// fix loading briefly flickering a dark box
emptyBuilder: (BuildContext context) => const SizedBox
.shrink(), // fix loading briefly showing no suggestions
), ),
minLines: minLines,
maxLines: maxLines,
keyboardType: keyboardType!,
textInputAction: textInputAction,
autofocus: autofocus!,
inputFormatters: [
LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
],
onSubmitted: (text) {
// fix for library for now
// it sets the types for the callback incorrectly
onSubmitted!(text);
},
decoration: decoration!,
onChanged: (text) {
// fix for the library for now
// it sets the types for the callback incorrectly
onChanged!(text);
},
textCapitalization: TextCapitalization.sentences,
), ),
suggestionsCallback: getSuggestions,
itemBuilder: (c, s) => buildSuggestion(c, s, Matrix.of(context).client),
onSelected: (Map<String, String?> suggestion) =>
insertSuggestion(context, suggestion),
errorBuilder: (BuildContext context, Object? error) =>
const SizedBox.shrink(),
loadingBuilder: (BuildContext context) => const SizedBox.shrink(),
// fix loading briefly flickering a dark box
emptyBuilder: (BuildContext context) =>
const SizedBox.shrink(), // fix loading briefly showing no suggestions
); );
} }
} }
class NewLineIntent extends Intent {}
class SubmitLineIntent extends Intent {}
class PasteLineIntent extends Intent {}

Loading…
Cancel
Save