Skip to content

Commit

Permalink
feat: Add CallbackGlobalShortcuts and GlobalShortcuts Widgets.
Browse files Browse the repository at this point in the history
  • Loading branch information
lijy91 committed Mar 27, 2024
1 parent badd689 commit 7422790
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

```yaml
dependencies:
hotkey_manager: ^0.2.1
hotkey_manager: ^0.2.2
```
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Add this to your package's pubspec.yaml file:

```yaml
dependencies:
hotkey_manager: ^0.2.1
hotkey_manager: ^0.2.2
```
Or
Expand Down
4 changes: 4 additions & 0 deletions packages/hotkey_manager/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.2

* feat: Add `CallbackGlobalShortcuts` and `GlobalShortcuts` Widgets.

## 0.2.1

* Fixed issue where modifiers do not work #50
Expand Down
37 changes: 34 additions & 3 deletions packages/hotkey_manager/example/lib/pages/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@ import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:hotkey_manager_example/widgets/record_hotkey_dialog.dart';
import 'package:preference_list/preference_list.dart';

class ExampleIntent extends Intent {}

class ExampleAction extends Action<ExampleIntent> {
@override
void invoke(covariant ExampleIntent intent) {
BotToast.showText(text: 'ExampleAction invoked');
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

Expand Down Expand Up @@ -139,13 +149,34 @@ class _HomePageState extends State<HomePage> {
);
}

@override
Widget build(BuildContext context) {
Widget _build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: _buildBody(context),
body: Column(
children: [
Expanded(
child: _buildBody(context),
),
],
),
);
}

@override
Widget build(BuildContext context) {
return Actions(
actions: <Type, Action<Intent>>{
ExampleIntent: ExampleAction(),
},
child: GlobalShortcuts(
shortcuts: {
const SingleActivator(LogicalKeyboardKey.keyA, alt: true):
ExampleIntent(),
},
child: _build(context),
),
);
}
}
7 changes: 4 additions & 3 deletions packages/hotkey_manager/lib/hotkey_manager.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'package:hotkey_manager/src/hotkey_manager.dart';
export 'package:hotkey_manager/src/widgets/global_shortcuts.dart';
export 'package:hotkey_manager/src/widgets/hotkey_recorder.dart';
export 'package:hotkey_manager/src/widgets/hotkey_virtual_view.dart';
export 'package:hotkey_manager_platform_interface/hotkey_manager_platform_interface.dart';
export './src/hotkey_manager.dart';
export './src/widgets/hotkey_recorder.dart';
export './src/widgets/hotkey_virtual_view.dart';
142 changes: 142 additions & 0 deletions packages/hotkey_manager/lib/src/widgets/global_shortcuts.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import 'package:flutter/widgets.dart';
import 'package:hotkey_manager/hotkey_manager.dart';

extension _SingleActivatorExtension on SingleActivator {
HotKey _toHotKey() {
List<HotKeyModifier> modifiers = [
if (control) HotKeyModifier.control,
if (shift) HotKeyModifier.shift,
if (alt) HotKeyModifier.alt,
if (meta) HotKeyModifier.meta,
];
return HotKey(
identifier: [
...modifiers.map((m) => m.name),
'${trigger.keyId}',
].join('+'),
key: trigger,
modifiers: modifiers,
scope: HotKeyScope.system,
);
}
}

class GlobalShortcuts extends StatefulWidget {
const GlobalShortcuts({
super.key,
required this.shortcuts,
required this.child,
});

final Map<SingleActivator, Intent> shortcuts;

final Widget child;

@override
State<GlobalShortcuts> createState() => _GlobalShortcutsState();
}

class _GlobalShortcutsState extends State<GlobalShortcuts> {
final List<HotKey> _registeredHotKeys = [];

@override
void initState() {
super.initState();
for (final entry in widget.shortcuts.entries) {
final hotKey = entry.key._toHotKey();
hotKeyManager.register(hotKey, keyDownHandler: _onKeyDown);
_registeredHotKeys.add(hotKey);
}
}

@override
void dispose() {
super.dispose();
for (final hotKey in _registeredHotKeys) {
hotKeyManager.unregister(hotKey);
}
_registeredHotKeys.clear();
}

void _onKeyDown(HotKey hotKey) {
final activator = widget.shortcuts.keys.firstWhere(
(activator) => activator._toHotKey().identifier == hotKey.identifier,
);
final Intent? matchedIntent = widget.shortcuts[activator];
if (matchedIntent != null) {
final Action<Intent>? action = Actions.maybeFind<Intent>(
context,
intent: matchedIntent,
);
if (action != null) {
final (bool enabled, Object? invokeResult) =
Actions.of(context).invokeActionIfEnabled(
action,
matchedIntent,
context,
);
if (enabled) {
action.toKeyEventResult(matchedIntent, invokeResult);
}
}
}
}

@override
Widget build(BuildContext context) {
return widget.child;
}
}

class CallbackGlobalShortcuts extends StatefulWidget {
const CallbackGlobalShortcuts({
super.key,
required this.bindings,
required this.child,
});

final Map<SingleActivator, VoidCallback> bindings;

final Widget child;

@override
State<CallbackGlobalShortcuts> createState() =>
_CallbackGlobalShortcutsState();
}

class _CallbackGlobalShortcutsState extends State<CallbackGlobalShortcuts> {
final List<HotKey> _registeredHotKeys = [];

Map<SingleActivator, VoidCallback> get bindings => widget.bindings;

@override
void initState() {
super.initState();
for (final entry in widget.bindings.entries) {
final hotKey = entry.key._toHotKey();
hotKeyManager.register(hotKey, keyDownHandler: _onKeyDown);
_registeredHotKeys.add(hotKey);
}
}

@override
void dispose() {
super.dispose();
for (final hotKey in _registeredHotKeys) {
hotKeyManager.unregister(hotKey);
}
_registeredHotKeys.clear();
}

void _onKeyDown(HotKey hotKey) {
final activator = bindings.keys.firstWhere(
(activator) => activator._toHotKey().identifier == hotKey.identifier,
);
bindings[activator]!.call();
}

@override
Widget build(BuildContext context) {
return widget.child;
}
}
2 changes: 1 addition & 1 deletion packages/hotkey_manager/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: hotkey_manager
description: This plugin allows Flutter desktop apps to defines system/inapp wide hotkey (i.e. shortcut).
version: 0.2.1
version: 0.2.2
homepage: https://github.com/leanflutter/hotkey_manager

platforms:
Expand Down

0 comments on commit 7422790

Please sign in to comment.