import 'package:animated_theme_switcher/animated_theme_switcher.dart'; import 'package:async_tools/async_tools.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:veilid_support/veilid_support.dart'; import 'init.dart'; import 'router/router.dart'; import 'settings/settings.dart'; import 'theme/theme.dart'; import 'tools/tools.dart'; import 'veilid_processor/veilid_processor.dart'; class ReloadThemeIntent extends Intent { const ReloadThemeIntent(); } class ChangeBrightnessIntent extends Intent { const ChangeBrightnessIntent(); } class ChangeColorIntent extends Intent { const ChangeColorIntent(); } class AttachDetachIntent extends Intent { const AttachDetachIntent(); } class DeveloperPageIntent extends Intent { const DeveloperPageIntent(); } class DisplayScaleUpIntent extends Intent { const DisplayScaleUpIntent(); } class DisplayScaleDownIntent extends Intent { const DisplayScaleDownIntent(); } class KeyboardShortcuts extends StatelessWidget { const KeyboardShortcuts({required this.child, super.key}); void reloadTheme(BuildContext context) { singleFuture(this, () async { log.info('Reloading theme'); await VeilidChatGlobalInit.loadAssetManifest(); final theme = PreferencesRepository.instance.value.themePreference.themeData(); if (context.mounted) { ThemeSwitcher.of(context).changeTheme(theme: theme); // Hack to reload translations final localizationDelegate = LocalizedApp.of(context).delegate; await LocalizationDelegate.create( fallbackLocale: localizationDelegate.fallbackLocale.toString(), supportedLocales: localizationDelegate.supportedLocales .map((x) => x.toString()) .toList()); } }); } void _changeBrightness(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; final oldBrightness = prefs.themePreference.brightnessPreference; final newBrightness = BrightnessPreference.values[ (oldBrightness.index + 1) % BrightnessPreference.values.length]; log.info('Changing brightness to $newBrightness'); final newPrefs = prefs.copyWith( themePreference: prefs.themePreference .copyWith(brightnessPreference: newBrightness)); await PreferencesRepository.instance.set(newPrefs); if (context.mounted) { ThemeSwitcher.of(context) .changeTheme(theme: newPrefs.themePreference.themeData()); } }); } void _changeColor(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; final oldColor = prefs.themePreference.colorPreference; final newColor = ColorPreference .values[(oldColor.index + 1) % ColorPreference.values.length]; log.info('Changing color to $newColor'); final newPrefs = prefs.copyWith( themePreference: prefs.themePreference.copyWith(colorPreference: newColor)); await PreferencesRepository.instance.set(newPrefs); if (context.mounted) { ThemeSwitcher.of(context) .changeTheme(theme: newPrefs.themePreference.themeData()); } }); } void _displayScaleUp(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; final oldIndex = displayScaleToIndex(prefs.themePreference.displayScale); if (oldIndex == maxDisplayScaleIndex) { return; } final newIndex = oldIndex + 1; final newDisplayScaleName = indexToDisplayScaleName(newIndex); log.info('Changing display scale to $newDisplayScaleName'); final newPrefs = prefs.copyWith( themePreference: prefs.themePreference .copyWith(displayScale: indexToDisplayScale(newIndex))); await PreferencesRepository.instance.set(newPrefs); if (context.mounted) { ThemeSwitcher.of(context) .changeTheme(theme: newPrefs.themePreference.themeData()); } }); } void _displayScaleDown(BuildContext context) { singleFuture(this, () async { final prefs = PreferencesRepository.instance.value; final oldIndex = displayScaleToIndex(prefs.themePreference.displayScale); if (oldIndex == 0) { return; } final newIndex = oldIndex - 1; final newDisplayScaleName = indexToDisplayScaleName(newIndex); log.info('Changing display scale to $newDisplayScaleName'); final newPrefs = prefs.copyWith( themePreference: prefs.themePreference .copyWith(displayScale: indexToDisplayScale(newIndex))); await PreferencesRepository.instance.set(newPrefs); if (context.mounted) { ThemeSwitcher.of(context) .changeTheme(theme: newPrefs.themePreference.themeData()); } }); } void _attachDetach(BuildContext context) { singleFuture(this, () async { if (ProcessorRepository.instance.processorConnectionState.isAttached) { log.info('Detaching'); await Veilid.instance.detach(); } else if (ProcessorRepository .instance.processorConnectionState.isDetached) { log.info('Attaching'); await Veilid.instance.attach(); } }); } void _developerPage(BuildContext context) { singleFuture(this, () async { final path = GoRouter.of(context).location(); if (path != '/developer') { await GoRouterHelper(context).push('/developer'); } }); } @override Widget build(BuildContext context) => ThemeSwitcher( builder: (context) => Shortcuts( shortcuts: { ////////////////////////// Reload Theme const SingleActivator( LogicalKeyboardKey.keyR, control: true, alt: true, ): const ReloadThemeIntent(), ////////////////////////// Switch Brightness const SingleActivator( LogicalKeyboardKey.keyB, control: true, alt: true, ): const ChangeBrightnessIntent(), ////////////////////////// Change Color const SingleActivator( LogicalKeyboardKey.keyC, control: true, alt: true, ): const ChangeColorIntent(), ////////////////////////// Attach/Detach if (kIsDebugMode) const SingleActivator( LogicalKeyboardKey.keyA, control: true, alt: true, ): const AttachDetachIntent(), ////////////////////////// Show Developer Page const SingleActivator( LogicalKeyboardKey.keyD, control: true, alt: true, ): const DeveloperPageIntent(), ////////////////////////// Display Scale Up SingleActivator( LogicalKeyboardKey.equal, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleUpIntent(), SingleActivator( LogicalKeyboardKey.equal, shift: true, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleUpIntent(), SingleActivator( LogicalKeyboardKey.add, shift: true, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleUpIntent(), SingleActivator( LogicalKeyboardKey.numpadAdd, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleUpIntent(), ////////////////////////// Display Scale Down SingleActivator( LogicalKeyboardKey.minus, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleDownIntent(), SingleActivator( LogicalKeyboardKey.numpadSubtract, meta: isMac || isiOS, control: !(isMac || isiOS), ): const DisplayScaleDownIntent(), }, child: Actions(actions: >{ ReloadThemeIntent: CallbackAction( onInvoke: (intent) => reloadTheme(context)), ChangeBrightnessIntent: CallbackAction( onInvoke: (intent) => _changeBrightness(context)), ChangeColorIntent: CallbackAction( onInvoke: (intent) => _changeColor(context)), AttachDetachIntent: CallbackAction( onInvoke: (intent) => _attachDetach(context)), DeveloperPageIntent: CallbackAction( onInvoke: (intent) => _developerPage(context)), DisplayScaleUpIntent: CallbackAction( onInvoke: (intent) => _displayScaleUp(context)), DisplayScaleDownIntent: CallbackAction( onInvoke: (intent) => _displayScaleDown(context)), }, child: Focus(autofocus: true, child: child)))); ///////////////////////////////////////////////////////// final Widget child; }