Skip to content

Commit

Permalink
Improve Keyboard Shortcuts (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Nov 28, 2024
2 parents 574da3a + f14cf16 commit fa2e5e1
Show file tree
Hide file tree
Showing 14 changed files with 1,143 additions and 279 deletions.
21 changes: 20 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import 'package:bluecherry_client/screens/multi_window/single_layout_window.dart
import 'package:bluecherry_client/screens/multi_window/window.dart';
import 'package:bluecherry_client/screens/players/live_player.dart';
import 'package:bluecherry_client/utils/app_links/app_links.dart' as app_links;
import 'package:bluecherry_client/utils/keyboard.dart';
import 'package:bluecherry_client/utils/logging.dart' as logging;
import 'package:bluecherry_client/utils/methods.dart';
import 'package:bluecherry_client/utils/storage.dart';
Expand Down Expand Up @@ -115,6 +116,7 @@ Future<void> main(List<String> args) async {
await LayoutsProvider.ensureInitialized();
await UpdateManager.ensureInitialized();
await EventsProvider.ensureInitialized();
await KeyboardBindings.ensureInitialized();
},
onLayoutScreen: (layout, theme) {
configureWindowTitle(layout.name);
Expand Down Expand Up @@ -332,6 +334,9 @@ class _UnityAppState extends State<UnityApp>
ChangeNotifierProvider<EventsProvider>.value(
value: EventsProvider.instance,
),
ChangeNotifierProvider<KeyboardBindings>.value(
value: KeyboardBindings.instance,
),
],
child: Consumer<SettingsProvider>(builder: (context, settings, _) {
return MaterialApp(
Expand Down Expand Up @@ -416,7 +421,21 @@ class _UnityAppState extends State<UnityApp>
builder: (context, child) {
Intl.defaultLocale = Localizations.localeOf(context).languageCode;

return child!;
final home = context.watch<HomeProvider>();
final bindings = context.watch<KeyboardBindings>();

return CallbackShortcuts(
bindings: navigatorObserver.isDialog
? {}
: {
...globalShortcuts(context, bindings),
if (home.tab == UnityTab.deviceGrid)
...layoutShortcuts(context, bindings),
if (home.tab == UnityTab.settings)
...settingsShortcuts(context, bindings),
},
child: child!,
);
},
);
}),
Expand Down
70 changes: 70 additions & 0 deletions lib/providers/home_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'package:bluecherry_client/providers/layouts_provider.dart';
import 'package:bluecherry_client/providers/server_provider.dart';
import 'package:bluecherry_client/providers/settings_provider.dart';
import 'package:bluecherry_client/providers/update_provider.dart';
import 'package:bluecherry_client/utils/constants.dart';
import 'package:bluecherry_client/utils/methods.dart';
import 'package:bluecherry_client/utils/video_player.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -104,6 +105,75 @@ enum UnityLoadingReason {
}
}

class NavigatorData {
/// The tab that this navigator data represents.
final UnityTab tab;
final IconData icon;
final IconData selectedIcon;
final String text;

const NavigatorData({
required this.tab,
required this.icon,
required this.selectedIcon,
required this.text,
});

static List<NavigatorData> of(BuildContext context) {
final loc = AppLocalizations.of(context);
final screenSize = MediaQuery.sizeOf(context);
final layout = context.read<LayoutsProvider>().currentLayout;

return [
NavigatorData(
tab: UnityTab.deviceGrid,
icon: Icons.window_outlined,
selectedIcon: Icons.window,
text: loc.screens(layout.name),
),
NavigatorData(
tab: UnityTab.eventsTimeline,
icon: Icons.subscriptions_outlined,
selectedIcon: Icons.subscriptions,
text: loc.eventsTimeline,
),
if (screenSize.width <= kMobileBreakpoint.width ||
Scaffold.hasDrawer(context))
NavigatorData(
tab: UnityTab.directCameraScreen,
icon: Icons.videocam_outlined,
selectedIcon: Icons.videocam,
text: loc.directCamera,
),
NavigatorData(
tab: UnityTab.eventsHistory,
icon: Icons.featured_play_list_outlined,
selectedIcon: Icons.featured_play_list,
text: loc.eventBrowser,
),
NavigatorData(
tab: UnityTab.addServer,
icon: Icons.dns_outlined,
selectedIcon: Icons.dns,
text: loc.addServer,
),
if (!kIsWeb)
NavigatorData(
tab: UnityTab.downloads,
icon: Icons.download_outlined,
selectedIcon: Icons.download,
text: loc.downloads,
),
NavigatorData(
tab: UnityTab.settings,
icon: Icons.settings_outlined,
selectedIcon: Icons.settings,
text: loc.settings,
),
];
}
}

class HomeProvider extends ChangeNotifier {
static final _instance = HomeProvider();
static HomeProvider get instance => _instance;
Expand Down
4 changes: 4 additions & 0 deletions lib/providers/layouts_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import 'package:bluecherry_client/providers/app_provider_interface.dart';
import 'package:bluecherry_client/utils/constants.dart';
import 'package:bluecherry_client/utils/storage.dart';
import 'package:bluecherry_client/utils/video_player.dart';
import 'package:bluecherry_client/widgets/collapsable_sidebar.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:unity_video_player/unity_video_player.dart';

class LayoutsProvider extends UnityProvider {
Expand Down Expand Up @@ -69,6 +71,8 @@ class LayoutsProvider extends UnityProvider {
save();
}

final sidebarKey = GlobalKey<CollapsableSidebarState>();

@override
Future<void> initialize() async {
await initializeStorage(kStorageDesktopLayouts);
Expand Down
7 changes: 7 additions & 0 deletions lib/providers/settings_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,13 @@ class SettingsProvider extends UnityProvider {
kShowNetworkUsage,
];

int _settingsIndex = 0;
int get settingsIndex => _settingsIndex;
set settingsIndex(int value) {
_settingsIndex = value;
notifyListeners();
}

/// Initializes the [SettingsProvider] instance & fetches state from `async`
/// `package:hive` method-calls. Called before [runApp].
static Future<SettingsProvider> ensureInitialized() async {
Expand Down
2 changes: 2 additions & 0 deletions lib/screens/events_timeline/desktop/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ class Timeline extends ChangeNotifier {
});
}

// TODO(bdlukaa): Only make it possible to seek between bounds.
// Currently, is is possible to seek before and after the day.
/// Seeks forward by [duration]
void seekForward([Duration duration = const Duration(seconds: 15)]) =>
seekTo(currentPosition + duration);
Expand Down
85 changes: 51 additions & 34 deletions lib/screens/events_timeline/events_playback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ class _EventsPlaybackState extends EventsScreenState<EventsPlayback> {
'${event.physicalKey}${event.physicalKey.debugName}',
);

final isTimelineValid = timeline!.tiles.isNotEmpty;

switch (event.logicalKey) {
case LogicalKeyboardKey.arrowRight:
timeline!.seekForward();
Expand Down Expand Up @@ -230,26 +232,18 @@ class _EventsPlaybackState extends EventsScreenState<EventsPlayback> {
return KeyEventResult.handled;
case LogicalKeyboardKey.mediaSkipForward:
case LogicalKeyboardKey.mediaTrackNext:
timeline!.seekToNextEvent();
return KeyEventResult.handled;
final nextEvent = timeline!.seekToNextEvent();
if (nextEvent != null) return KeyEventResult.handled;
case LogicalKeyboardKey.mediaSkipBackward:
case LogicalKeyboardKey.mediaTrackPrevious:
timeline!.seekToPreviousEvent();
return KeyEventResult.handled;
final previousEvent = timeline!.seekToPreviousEvent();
if (previousEvent != null) return KeyEventResult.handled;
case LogicalKeyboardKey.mediaStepForward:
timeline!.stepForward();
return KeyEventResult.handled;
case LogicalKeyboardKey.mediaStepBackward:
timeline!.stepBackward();
return KeyEventResult.handled;
case LogicalKeyboardKey.home:
case LogicalKeyboardKey.numpad0:
case LogicalKeyboardKey.digit0:
timeline!.seekTo(Duration.zero);
return KeyEventResult.handled;
case LogicalKeyboardKey.end:
timeline!.seekTo(timeline!.endPosition);
return KeyEventResult.handled;
case LogicalKeyboardKey.keyM:
if (timeline!.isMuted) {
timeline!.volume = 1.0;
Expand All @@ -263,47 +257,70 @@ class _EventsPlaybackState extends EventsScreenState<EventsPlayback> {
case LogicalKeyboardKey.arrowDown:
timeline!.volume -= 0.1;
return KeyEventResult.handled;

case LogicalKeyboardKey.home:
case LogicalKeyboardKey.numpad0:
case LogicalKeyboardKey.digit0:
timeline!.seekTo(Duration.zero);
return KeyEventResult.handled;
case LogicalKeyboardKey.end:
timeline!.seekTo(timeline!.endPosition);
return KeyEventResult.handled;
case LogicalKeyboardKey.numpad1:
case LogicalKeyboardKey.digit1:
timeline!.seekTo(timeline!.endPosition * 0.1);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.1);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad2:
case LogicalKeyboardKey.digit2:
timeline!.seekTo(timeline!.endPosition * 0.2);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.2);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad3:
case LogicalKeyboardKey.digit3:
timeline!.seekTo(timeline!.endPosition * 0.3);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.3);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad4:
case LogicalKeyboardKey.digit4:
timeline!.seekTo(timeline!.endPosition * 0.4);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.4);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad5:
case LogicalKeyboardKey.digit5:
timeline!.seekTo(timeline!.endPosition * 0.5);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.5);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad6:
case LogicalKeyboardKey.digit6:
timeline!.seekTo(timeline!.endPosition * 0.6);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.6);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad7:
case LogicalKeyboardKey.digit7:
timeline!.seekTo(timeline!.endPosition * 0.7);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.7);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad8:
case LogicalKeyboardKey.digit8:
timeline!.seekTo(timeline!.endPosition * 0.8);
return KeyEventResult.handled;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.8);
return KeyEventResult.handled;
}
case LogicalKeyboardKey.numpad9:
case LogicalKeyboardKey.digit9:
timeline!.seekTo(timeline!.endPosition * 0.9);
return KeyEventResult.handled;

default:
return KeyEventResult.ignored;
if (isTimelineValid) {
timeline!.seekTo(timeline!.endPosition * 0.9);
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
},
child: LayoutBuilder(builder: (context, constraints) {
final hasDrawer = Scaffold.hasDrawer(context);
Expand Down
Loading

0 comments on commit fa2e5e1

Please sign in to comment.