Keyboard shortcuts
This commit is contained in:
parent
9a67ce6c17
commit
5da257b390
|
|
@ -10,33 +10,15 @@ class AppBuilder extends StatefulWidget {
|
||||||
|
|
||||||
static AppBuilderState of(BuildContext context) {
|
static AppBuilderState of(BuildContext context) {
|
||||||
return context.findAncestorStateOfType<AppBuilderState>();
|
return context.findAncestorStateOfType<AppBuilderState>();
|
||||||
//return context.ancestorStateOfType(const TypeMatcher<AppBuilderState>());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppBuilderState extends State<AppBuilder> {
|
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() {
|
void rebuild() {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void runCommand(BuildContext context) {
|
void runCommand(BuildContext context) {
|
||||||
print('### RUN COMMAND: $_command ###');
|
|
||||||
/*
|
/*
|
||||||
final store = StoreProvider.of<AppState>(context);
|
final store = StoreProvider.of<AppState>(context);
|
||||||
final company = store.state.company;
|
final company = store.state.company;
|
||||||
|
|
@ -120,22 +102,6 @@ class AppBuilderState extends State<AppBuilder> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RawKeyboardListener(
|
return widget.builder(context);
|
||||||
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 = '');
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
@ -56,7 +56,8 @@ class EditScaffold extends StatelessWidget {
|
||||||
onSavePressed: (context) {
|
onSavePressed: (context) {
|
||||||
// Clear focus now to prevent un-focus after save from
|
// Clear focus now to prevent un-focus after save from
|
||||||
// marking the form as changed and to hide the keyboard
|
// marking the form as changed and to hide the keyboard
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus(
|
||||||
|
disposition: UnfocusDisposition.previouslyFocusedChild);
|
||||||
|
|
||||||
onSavePressed(context);
|
onSavePressed(context);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,8 @@ class _ListFilterState extends State<ListFilter> {
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_filterController.text = '';
|
_filterController.text = '';
|
||||||
_focusNode.unfocus();
|
_focusNode.unfocus(
|
||||||
|
disposition: UnfocusDisposition.previouslyFocusedChild);
|
||||||
widget.onFilterChanged(null);
|
widget.onFilterChanged(null);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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/reports/reports_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
|
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/ui/pref_state.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/blank_screen.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/change_layout_banner.dart';
|
import 'package:invoiceninja_flutter/ui/app/change_layout_banner.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/history_drawer_vm.dart';
|
import 'package:invoiceninja_flutter/ui/app/history_drawer_vm.dart';
|
||||||
|
|
@ -324,6 +325,7 @@ class MainScreen extends StatelessWidget {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
child: AppShortcuts(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: FocusTraversalGroup(
|
child: FocusTraversalGroup(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderTraversalPolicy(),
|
||||||
|
|
@ -341,6 +343,7 @@ class MainScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue