fix: some keys conflict with board shortcuts (#5460)

This commit is contained in:
Lucas.Xu 2024-06-04 11:37:06 +08:00 committed by GitHub
parent 1e485188eb
commit 57d4652824
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 125 additions and 59 deletions

View File

@ -14,6 +14,7 @@ import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart';
import 'package:appflowy/plugins/database/widgets/card/card_bloc.dart'; import 'package:appflowy/plugins/database/widgets/card/card_bloc.dart';
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/desktop_board_card_cell_style.dart'; import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/desktop_board_card_cell_style.dart';
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart'; import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
import 'package:appflowy/plugins/shared/callback_shortcuts.dart';
import 'package:appflowy/shared/conditional_listenable_builder.dart'; import 'package:appflowy/shared/conditional_listenable_builder.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
@ -28,6 +29,7 @@ import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart' hide Card; import 'package:flutter/material.dart' hide Card;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import '../../widgets/card/card.dart'; import '../../widgets/card/card.dart';
import '../../widgets/cell/card_cell_builder.dart'; import '../../widgets/cell/card_cell_builder.dart';
@ -320,64 +322,67 @@ class _BoardContentState extends State<_BoardContent> {
}, },
), ),
], ],
child: FocusScope( child: Provider(
autofocus: true, create: (context) => AFCallbackShortcutsProvider(),
child: BoardShortcutContainer( child: FocusScope(
focusScope: widget.focusScope, autofocus: true,
child: Padding( child: BoardShortcutContainer(
padding: const EdgeInsets.only(top: 8.0), focusScope: widget.focusScope,
child: AppFlowyBoard( child: Padding(
boardScrollController: scrollManager, padding: const EdgeInsets.only(top: 8.0),
scrollController: scrollController, child: AppFlowyBoard(
controller: context.read<BoardBloc>().boardController, boardScrollController: scrollManager,
groupConstraints: const BoxConstraints.tightFor(width: 256), scrollController: scrollController,
config: config, controller: context.read<BoardBloc>().boardController,
leading: HiddenGroupsColumn(margin: config.groupHeaderPadding), groupConstraints: const BoxConstraints.tightFor(width: 256),
trailing: context config: config,
.read<BoardBloc>() leading: HiddenGroupsColumn(margin: config.groupHeaderPadding),
.groupingFieldType trailing: context
?.canCreateNewGroup ?? .read<BoardBloc>()
false .groupingFieldType
? BoardTrailing(scrollController: scrollController) ?.canCreateNewGroup ??
: const HSpace(40), false
headerBuilder: (_, groupData) => BlocProvider<BoardBloc>.value( ? BoardTrailing(scrollController: scrollController)
value: context.read<BoardBloc>(), : const HSpace(40),
child: BoardColumnHeader( headerBuilder: (_, groupData) => BlocProvider<BoardBloc>.value(
groupData: groupData, value: context.read<BoardBloc>(),
margin: config.groupHeaderPadding, child: BoardColumnHeader(
groupData: groupData,
margin: config.groupHeaderPadding,
),
), ),
), footerBuilder: (_, groupData) => MultiBlocProvider(
footerBuilder: (_, groupData) => MultiBlocProvider( providers: [
providers: [ BlocProvider.value(
BlocProvider.value( value: context.read<BoardBloc>(),
value: context.read<BoardBloc>(), ),
BlocProvider.value(
value: context.read<BoardActionsCubit>(),
),
],
child: BoardColumnFooter(
columnData: groupData,
boardConfig: config,
scrollManager: scrollManager,
), ),
BlocProvider.value(
value: context.read<BoardActionsCubit>(),
),
],
child: BoardColumnFooter(
columnData: groupData,
boardConfig: config,
scrollManager: scrollManager,
), ),
), cardBuilder: (_, column, columnItem) => MultiBlocProvider(
cardBuilder: (_, column, columnItem) => MultiBlocProvider( key: ValueKey("board_card_${column.id}_${columnItem.id}"),
key: ValueKey("board_card_${column.id}_${columnItem.id}"), providers: [
providers: [ BlocProvider<BoardBloc>.value(
BlocProvider<BoardBloc>.value( value: context.read<BoardBloc>(),
value: context.read<BoardBloc>(), ),
BlocProvider.value(
value: context.read<BoardActionsCubit>(),
),
],
child: _BoardCard(
afGroupData: column,
groupItem: columnItem as GroupItem,
boardConfig: config,
notifier: widget.focusScope,
cellBuilder: cellBuilder,
), ),
BlocProvider.value(
value: context.read<BoardActionsCubit>(),
),
],
child: _BoardCard(
afGroupData: column,
groupItem: columnItem as GroupItem,
boardConfig: config,
notifier: widget.focusScope,
cellBuilder: cellBuilder,
), ),
), ),
), ),

View File

@ -3,6 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/board/application/board_bloc.dart'; import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/plugins/shared/callback_shortcuts.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_board/appflowy_board.dart'; import 'package:appflowy_board/appflowy_board.dart';
@ -30,6 +31,8 @@ class BoardColumnHeader extends StatefulWidget {
class _BoardColumnHeaderState extends State<BoardColumnHeader> { class _BoardColumnHeaderState extends State<BoardColumnHeader> {
final FocusNode _focusNode = FocusNode(); final FocusNode _focusNode = FocusNode();
final FocusNode _keyboardListenerFocusNode = FocusNode();
late final AFCallbackShortcutsProvider _shortcutsProvider;
late final TextEditingController _controller = late final TextEditingController _controller =
TextEditingController.fromValue( TextEditingController.fromValue(
@ -44,16 +47,23 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_shortcutsProvider = context.read<AFCallbackShortcutsProvider>();
_focusNode.addListener(() { _focusNode.addListener(() {
if (!_focusNode.hasFocus) { if (!_focusNode.hasFocus) {
_saveEdit(); _saveEdit();
} }
}); });
_keyboardListenerFocusNode.addListener(() {
_shortcutsProvider.isShortcutsEnabled.value =
!_keyboardListenerFocusNode.hasFocus;
});
} }
@override @override
void dispose() { void dispose() {
_shortcutsProvider.isShortcutsEnabled.value = true;
_focusNode.dispose(); _focusNode.dispose();
_keyboardListenerFocusNode.dispose();
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();
} }
@ -149,7 +159,7 @@ class _BoardColumnHeaderState extends State<BoardColumnHeader> {
Widget _buildTextField(BuildContext context) { Widget _buildTextField(BuildContext context) {
return Expanded( return Expanded(
child: KeyboardListener( child: KeyboardListener(
focusNode: FocusNode(), focusNode: _keyboardListenerFocusNode,
onKeyEvent: (event) { onKeyEvent: (event) {
if ([LogicalKeyboardKey.enter, LogicalKeyboardKey.escape] if ([LogicalKeyboardKey.enter, LogicalKeyboardKey.escape]
.contains(event.logicalKey)) { .contains(event.logicalKey)) {

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'package:appflowy/plugins/database/board/application/board_actions_bloc.dart'; import 'package:appflowy/plugins/database/board/application/board_actions_bloc.dart';
import 'package:appflowy/plugins/database/board/application/board_bloc.dart'; import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
import 'package:appflowy/plugins/shared/callback_shortcuts.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -20,7 +21,9 @@ class BoardShortcutContainer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CallbackShortcuts( return AFCallbackShortcuts(
canAcceptEvent: (_, __) =>
context.read<AFCallbackShortcutsProvider>().isShortcutsEnabled.value,
bindings: { bindings: {
const SingleActivator(LogicalKeyboardKey.arrowUp): const SingleActivator(LogicalKeyboardKey.arrowUp):
focusScope.focusPrevious, focusScope.focusPrevious,
@ -93,9 +96,9 @@ class BoardShortcutContainer extends StatelessWidget {
if (focusScope.value.length != 1) { if (focusScope.value.length != 1) {
return; return;
} }
context context
.read<BoardActionsCubit>() .read<BoardActionsCubit>()
.openCardWithRowId(focusScope.value.first.rowId); .openCardWithRowId(focusScope.value.first.rowId);
} }
void _shitEnterHandler(BuildContext context) { void _shitEnterHandler(BuildContext context) {

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AFCallbackShortcutsProvider {
final ValueNotifier<bool> isShortcutsEnabled = ValueNotifier(true);
}
class AFCallbackShortcuts extends StatelessWidget {
const AFCallbackShortcuts({
super.key,
required this.bindings,
required this.canAcceptEvent,
required this.child,
});
final Map<ShortcutActivator, VoidCallback> bindings;
final bool Function(FocusNode node, KeyEvent event) canAcceptEvent;
final Widget child;
bool _applyKeyEventBinding(ShortcutActivator activator, KeyEvent event) {
if (activator.accepts(event, HardwareKeyboard.instance)) {
bindings[activator]!.call();
return true;
}
return false;
}
@override
Widget build(BuildContext context) {
return Focus(
canRequestFocus: false,
skipTraversal: true,
onKeyEvent: (FocusNode node, KeyEvent event) {
if (!canAcceptEvent(node, event)) {
return KeyEventResult.ignored;
}
KeyEventResult result = KeyEventResult.ignored;
for (final ShortcutActivator activator in bindings.keys) {
result = _applyKeyEventBinding(activator, event)
? KeyEventResult.handled
: result;
}
return result;
},
child: child,
);
}
}