Browse Source

chore: click enter to exist edit state

appflowy 3 năm trước cách đây
mục cha
commit
1a83cb65ed

+ 2 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -15,6 +15,7 @@ import 'layout/sizes.dart';
 import 'widgets/row/grid_row.dart';
 import 'widgets/footer/grid_footer.dart';
 import 'widgets/header/grid_header.dart';
+import 'widgets/shortcuts.dart';
 import 'widgets/toolbar/grid_toolbar.dart';
 
 class GridPage extends StatefulWidget {
@@ -40,7 +41,7 @@ class _GridPageState extends State<GridPage> {
           return state.loadingState.map(
             loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
             finish: (result) => result.successOrFail.fold(
-              (_) => const FlowyGrid(),
+              (_) => const GridShortcuts(child: FlowyGrid()),
               (err) => FlowyErrorPage(err.toString()),
             ),
           );

+ 3 - 3
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart

@@ -18,8 +18,8 @@ abstract class GridCellAccessory implements Widget {
 
 typedef AccessoryBuilder = List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext);
 
-abstract class AccessoryWidget extends Widget {
-  const AccessoryWidget({Key? key}) : super(key: key);
+abstract class CellAccessory extends Widget {
+  const CellAccessory({Key? key}) : super(key: key);
 
   // The hover will show if the onFocus's value is true
   ValueNotifier<bool>? get isFocus;
@@ -28,7 +28,7 @@ abstract class AccessoryWidget extends Widget {
 }
 
 class AccessoryHover extends StatefulWidget {
-  final AccessoryWidget child;
+  final CellAccessory child;
   final EdgeInsets contentPadding;
   const AccessoryHover({
     required this.child,

+ 16 - 11
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart

@@ -8,6 +8,7 @@ import 'package:provider/provider.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'cell_accessory.dart';
+import 'cell_shortcuts.dart';
 import 'checkbox_cell.dart';
 import 'date_cell/date_cell.dart';
 import 'number_cell.dart';
@@ -48,7 +49,7 @@ class BlankCell extends StatelessWidget {
   }
 }
 
-abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget, CellContainerFocustable {
+abstract class GridCellWidget extends StatefulWidget implements CellAccessory, CellFocustable, CellShortcuts {
   GridCellWidget({Key? key}) : super(key: key);
 
   @override
@@ -58,27 +59,30 @@ abstract class GridCellWidget extends StatefulWidget implements AccessoryWidget,
   List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null;
 
   @override
-  final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus();
+  final GridCellFocusListener beginFocus = GridCellFocusListener();
+
+  @override
+  final Map<CellKeyboardKey, CellKeyboardAction> keyboardActionHandlers = {};
 }
 
 abstract class GridCellState<T extends GridCellWidget> extends State<T> {
   @override
   void initState() {
-    widget.requestBeginFocus.setListener(() => requestBeginFocus());
+    widget.beginFocus.setListener(() => requestBeginFocus());
     super.initState();
   }
 
   @override
   void didUpdateWidget(covariant T oldWidget) {
     if (oldWidget != this) {
-      widget.requestBeginFocus.setListener(() => requestBeginFocus());
+      widget.beginFocus.setListener(() => requestBeginFocus());
     }
     super.didUpdateWidget(oldWidget);
   }
 
   @override
   void dispose() {
-    widget.requestBeginFocus.removeAllListener();
+    widget.beginFocus.removeAllListener();
     super.dispose();
   }
 
@@ -90,6 +94,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
 
   @override
   void initState() {
+    widget.keyboardActionHandlers[CellKeyboardKey.onEnter] = () => focusNode.unfocus();
     _listenOnFocusNodeChanged();
     super.initState();
   }
@@ -104,6 +109,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
 
   @override
   void dispose() {
+    widget.keyboardActionHandlers.remove(CellKeyboardKey.onEnter);
     focusNode.removeAllListener();
     focusNode.dispose();
     super.dispose();
@@ -127,7 +133,7 @@ abstract class GridFocusNodeCellState<T extends GridCellWidget> extends GridCell
   Future<void> focusChanged() async {}
 }
 
-class GridCellRequestBeginFocus extends ChangeNotifier {
+class GridCellFocusListener extends ChangeNotifier {
   VoidCallback? _listener;
 
   void setListener(VoidCallback listener) {
@@ -194,9 +200,8 @@ class CellStateNotifier extends ChangeNotifier {
   bool get onEnter => _onEnter;
 }
 
-abstract class CellContainerFocustable {
-  // Listen on the requestBeginFocus if the
-  GridCellRequestBeginFocus get requestBeginFocus;
+abstract class CellFocustable {
+  GridCellFocusListener get beginFocus;
 }
 
 class CellContainer extends StatelessWidget {
@@ -220,7 +225,7 @@ class CellContainer extends StatelessWidget {
       child: Selector<CellStateNotifier, bool>(
         selector: (context, notifier) => notifier.isFocus,
         builder: (context, isFocus, _) {
-          Widget container = Center(child: child);
+          Widget container = Center(child: GridCellShortcuts(child: child));
           child.isFocus.addListener(() {
             Provider.of<CellStateNotifier>(context, listen: false).isFocus = child.isFocus.value;
           });
@@ -235,7 +240,7 @@ class CellContainer extends StatelessWidget {
 
           return GestureDetector(
             behavior: HitTestBehavior.translucent,
-            onTap: () => child.requestBeginFocus.notify(),
+            onTap: () => child.beginFocus.notify(),
             child: Container(
               constraints: BoxConstraints(maxWidth: width, minHeight: 46),
               decoration: _makeBoxDecoration(context, isFocus),

+ 47 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_shortcuts.dart

@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+typedef CellKeyboardAction = VoidCallback;
+
+enum CellKeyboardKey {
+  onEnter,
+}
+
+abstract class CellShortcuts extends Widget {
+  const CellShortcuts({Key? key}) : super(key: key);
+
+  Map<CellKeyboardKey, CellKeyboardAction> get keyboardActionHandlers;
+}
+
+class GridCellShortcuts extends StatelessWidget {
+  final CellShortcuts child;
+  const GridCellShortcuts({required this.child, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Shortcuts(
+      shortcuts: {LogicalKeySet(LogicalKeyboardKey.enter): const GridCellEnterIdent()},
+      child: Actions(
+        actions: {GridCellEnterIdent: GridCellEnterAction(child: child)},
+        child: child,
+      ),
+    );
+  }
+}
+
+class GridCellEnterIdent extends Intent {
+  const GridCellEnterIdent();
+}
+
+class GridCellEnterAction extends Action<GridCellEnterIdent> {
+  final CellShortcuts child;
+  GridCellEnterAction({required this.child});
+
+  @override
+  void invoke(covariant GridCellEnterIdent intent) {
+    final callback = child.keyboardActionHandlers[CellKeyboardKey.onEnter];
+    if (callback != null) {
+      callback();
+    }
+  }
+}

+ 1 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart

@@ -43,6 +43,7 @@ class _GridTextCellState extends GridFocusNodeCellState<GridTextCell> {
     _cellBloc = getIt<TextCellBloc>(param1: cellContext);
     _cellBloc.add(const TextCellEvent.initial());
     _controller = TextEditingController(text: _cellBloc.state.content);
+
     super.initState();
   }
 

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

@@ -154,7 +154,7 @@ class _RowDetailCell extends StatelessWidget {
 
     final gesture = GestureDetector(
       behavior: HitTestBehavior.translucent,
-      onTap: () => cell.requestBeginFocus.notify(),
+      onTap: () => cell.beginFocus.notify(),
       child: AccessoryHover(
         child: cell,
         contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),

+ 58 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/shortcuts.dart

@@ -0,0 +1,58 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class GridShortcuts extends StatelessWidget {
+  final Widget child;
+  const GridShortcuts({required this.child, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Shortcuts(
+      shortcuts: bindKeys([]),
+      child: Actions(
+        dispatcher: LoggingActionDispatcher(),
+        actions: const {},
+        child: child,
+      ),
+    );
+  }
+}
+
+Map<ShortcutActivator, Intent> bindKeys(List<LogicalKeyboardKey> keys) {
+  return {for (var key in keys) LogicalKeySet(key): KeyboardKeyIdent(key)};
+}
+
+Map<Type, Action<Intent>> bindActions() {
+  return {
+    KeyboardKeyIdent: KeyboardBindingAction(),
+  };
+}
+
+class KeyboardKeyIdent extends Intent {
+  final KeyboardKey key;
+
+  const KeyboardKeyIdent(this.key);
+}
+
+class KeyboardBindingAction extends Action<KeyboardKeyIdent> {
+  KeyboardBindingAction();
+
+  @override
+  void invoke(covariant KeyboardKeyIdent intent) {
+    // print(intent);
+  }
+}
+
+class LoggingActionDispatcher extends ActionDispatcher {
+  @override
+  Object? invokeAction(
+    covariant Action<Intent> action,
+    covariant Intent intent, [
+    BuildContext? context,
+  ]) {
+    // print('Action invoked: $action($intent) from $context');
+    super.invokeAction(action, intent, context);
+
+    return null;
+  }
+}