Keyboard shortcuts

This commit is contained in:
Hillel Coren 2020-10-07 14:40:45 +03:00
parent 9a67ce6c17
commit 5da257b390
5 changed files with 132 additions and 51 deletions

View File

@ -10,33 +10,15 @@ class AppBuilder extends StatefulWidget {
static AppBuilderState of(BuildContext context) {
return context.findAncestorStateOfType<AppBuilderState>();
//return context.ancestorStateOfType(const TypeMatcher<AppBuilderState>());
}
}
class AppBuilderState extends State<AppBuilder> {
FocusNode _focusNode;
String _command = '';
@override
void initState() {
super.initState();
_focusNode = new FocusNode();
_focusNode.requestFocus(null);
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void rebuild() {
setState(() {});
}
void runCommand(BuildContext context) {
print('### RUN COMMAND: $_command ###');
/*
final store = StoreProvider.of<AppState>(context);
final company = store.state.company;
@ -120,22 +102,6 @@ class AppBuilderState extends State<AppBuilder> {
@override
Widget build(BuildContext context) {
return RawKeyboardListener(
child: widget.builder(context),
focusNode: _focusNode,
onKey: (event) {
if (kReleaseMode) {
return;
}
_command = '';
/*
_command += event.logicalKey.keyLabel;
print(
'onKey: ${event.logicalKey.keyLabel}, hasFoucs: ${_focusNode.hasFocus}, hasPrimaryFocus: ${_focusNode.hasPrimaryFocus}');
runCommand(context);
Timer(Duration(seconds: 1), () => _command = '');
*/
},
);
return widget.builder(context);
}
}

View File

@ -0,0 +1,110 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
class AppShortcuts extends StatefulWidget {
const AppShortcuts({Key key, this.child}) : super(key: key);
final Widget child;
@override
AppShortcutsState createState() => new AppShortcutsState();
static AppShortcutsState of(BuildContext context) {
return context.findAncestorStateOfType<AppShortcutsState>();
}
}
class AppShortcutsState extends State<AppShortcuts> {
final _shortcuts = <LogicalKeySet, _ShortcutIntent>{
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyI):
_ShortcutIntent.create(EntityType.invoice),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyI):
_ShortcutIntent.list(EntityType.invoice),
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyC):
_ShortcutIntent.create(EntityType.client),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyC):
_ShortcutIntent.list(EntityType.client),
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyT):
_ShortcutIntent.create(EntityType.task),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyT):
_ShortcutIntent.list(EntityType.task),
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyE):
_ShortcutIntent.create(EntityType.expense),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyE):
_ShortcutIntent.list(EntityType.expense),
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyP):
_ShortcutIntent.create(EntityType.payment),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyP):
_ShortcutIntent.list(EntityType.payment),
LogicalKeySet(LogicalKeyboardKey.keyN, LogicalKeyboardKey.keyQ):
_ShortcutIntent.create(EntityType.quote),
LogicalKeySet(LogicalKeyboardKey.keyL, LogicalKeyboardKey.keyQ):
_ShortcutIntent.list(EntityType.quote),
if (!kIsWeb)
LogicalKeySet(LogicalKeyboardKey.alt, LogicalKeyboardKey.arrowLeft):
_ShortcutIntent.back(),
};
void rebuild() {
setState(() {});
}
@override
Widget build(BuildContext context) {
final _actions = <Type, Action<Intent>>{
_ShortcutIntent: CallbackAction<_ShortcutIntent>(
onInvoke: (_ShortcutIntent intent) {
switch (intent.intentType) {
case _ShortcutIntentType.create:
createEntityByType(
context: context, entityType: intent.entityType);
break;
case _ShortcutIntentType.list:
viewEntitiesByType(
context: context, entityType: intent.entityType);
break;
case _ShortcutIntentType.back:
Navigator.of(context).maybePop();
break;
}
return null;
},
),
};
return widget.child;
return FocusableActionDetector(
child: widget.child,
actions: _actions,
shortcuts: _shortcuts,
autofocus: false,
);
}
}
class _ShortcutIntent extends Intent {
const _ShortcutIntent({this.intentType, this.entityType});
const _ShortcutIntent.create(this.entityType)
: intentType = _ShortcutIntentType.create;
const _ShortcutIntent.list(this.entityType)
: intentType = _ShortcutIntentType.list;
const _ShortcutIntent.back()
: intentType = _ShortcutIntentType.back,
entityType = null;
final _ShortcutIntentType intentType;
final EntityType entityType;
}
enum _ShortcutIntentType {
create,
back,
list,
}

View File

@ -56,7 +56,8 @@ class EditScaffold extends StatelessWidget {
onSavePressed: (context) {
// Clear focus now to prevent un-focus after save from
// marking the form as changed and to hide the keyboard
FocusScope.of(context).unfocus();
FocusScope.of(context).unfocus(
disposition: UnfocusDisposition.previouslyFocusedChild);
onSavePressed(context);

View File

@ -128,7 +128,8 @@ class _ListFilterState extends State<ListFilter> {
),
onPressed: () {
_filterController.text = '';
_focusNode.unfocus();
_focusNode.unfocus(
disposition: UnfocusDisposition.previouslyFocusedChild);
widget.onFilterChanged(null);
},
)

View File

@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
import 'package:invoiceninja_flutter/redux/reports/reports_actions.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/ui/app/app_shortcuts.dart';
import 'package:invoiceninja_flutter/ui/app/blank_screen.dart';
import 'package:invoiceninja_flutter/ui/app/change_layout_banner.dart';
import 'package:invoiceninja_flutter/ui/app/history_drawer_vm.dart';
@ -324,20 +325,22 @@ class MainScreen extends StatelessWidget {
return false;
},
child: SafeArea(
child: FocusTraversalGroup(
policy: WidgetOrderTraversalPolicy(),
child: ChangeLayoutBanner(
appLayout: prefState.appLayout,
suggestedLayout: AppLayout.desktop,
child: Row(children: <Widget>[
if (prefState.showMenu) MenuDrawerBuilder(),
Expanded(
child: AppBorder(
child: screen,
isLeft: prefState.showMenu,
)),
]),
child: AppShortcuts(
child: SafeArea(
child: FocusTraversalGroup(
policy: WidgetOrderTraversalPolicy(),
child: ChangeLayoutBanner(
appLayout: prefState.appLayout,
suggestedLayout: AppLayout.desktop,
child: Row(children: <Widget>[
if (prefState.showMenu) MenuDrawerBuilder(),
Expanded(
child: AppBorder(
child: screen,
isLeft: prefState.showMenu,
)),
]),
),
),
),
),