feat: Customizable shortcuts/hotkeys (#439)

This implements the logic for allowing hotkeys with modifiers.
Implemented globalhotkeys and videocontrol hotkeys
Also implements saving the forward backwards seconds to the user.

Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
PartyDonut 2025-08-08 16:36:50 +02:00 committed by GitHub
parent 23385d8e62
commit fa30e634b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1360 additions and 162 deletions

View file

@ -14,6 +14,7 @@ import 'package:fladder/providers/settings/video_player_settings_provider.dart';
import 'package:fladder/providers/user_provider.dart';
import 'package:fladder/screens/settings/settings_list_tile.dart';
import 'package:fladder/screens/settings/settings_scaffold.dart';
import 'package:fladder/screens/settings/widgets/key_listener.dart';
import 'package:fladder/screens/settings/widgets/settings_label_divider.dart';
import 'package:fladder/screens/settings/widgets/settings_list_group.dart';
import 'package:fladder/screens/settings/widgets/settings_message_box.dart';
@ -43,6 +44,8 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
final connectionState = ref.watch(connectivityStatusProvider);
final userSettings = ref.watch(userProvider.select((value) => value?.userSettings));
return SettingsScaffold(
label: context.localized.settingsPlayerTitle,
items: [
@ -169,6 +172,70 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
),
]),
const SizedBox(height: 12),
...settingsListGroup(
context,
SettingsLabelDivider(label: context.localized.shortCuts),
[
if (userSettings != null)
SettingsListTile(
label: Text(context.localized.skipBackLength),
trailing: SizedBox(
width: 125,
child: IntInputField(
suffix: context.localized.seconds(10),
controller: TextEditingController(text: userSettings.skipBackDuration.inSeconds.toString()),
onSubmitted: (value) {
if (value != null) {
ref.read(userProvider.notifier).setBackwardSpeed(value);
}
},
)),
),
SettingsListTile(
label: Text(context.localized.skipForwardLength),
trailing: SizedBox(
width: 125,
child: IntInputField(
suffix: context.localized.seconds(10),
controller: TextEditingController(text: userSettings!.skipForwardDuration.inSeconds.toString()),
onSubmitted: (value) {
if (value != null) {
ref.read(userProvider.notifier).setForwardSpeed(value);
}
},
)),
),
if (AdaptiveLayout.inputDeviceOf(context) == InputDevice.pointer)
...VideoHotKeys.values.map(
(entry) {
final currentEntry = videoSettings.hotKeys[entry];
final defaultEntry = videoSettings.defaultShortCuts[entry]!;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Expanded(
child: Text(
entry.label(context),
style: Theme.of(context).textTheme.titleLarge,
),
),
Flexible(
child: KeyCombinationWidget(
currentKey: currentEntry,
defaultKey: defaultEntry,
onChanged: (value) =>
ref.read(videoPlayerSettingsProvider.notifier).setShortcuts(MapEntry(entry, value)),
),
)
],
),
);
},
)
],
),
const SizedBox(height: 12),
...settingsListGroup(context, SettingsLabelDivider(label: context.localized.playbackTrackSelection), [
SettingsListTile(
label: Text(context.localized.rememberAudioSelections),