chore: click enter to exist edit state

This commit is contained in:
appflowy 2022-06-04 11:04:43 +08:00
parent 46f69ee00d
commit 1a83cb65ed
7 changed files with 128 additions and 16 deletions

View File

@ -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()),
),
);

View File

@ -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,

View File

@ -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),

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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),

View File

@ -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;
}
}