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

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:screen_brightness/screen_brightness.dart';
import 'package:fladder/models/settings/key_combinations.dart';
import 'package:fladder/models/settings/video_player_settings.dart';
import 'package:fladder/providers/shared_provider.dart';
import 'package:fladder/providers/video_player_provider.dart';
@ -67,4 +69,51 @@ class VideoPlayerSettingsProviderNotifier extends StateNotifier<VideoPlayerSetti
void toggleOrientation(Set<DeviceOrientation>? orientation) =>
state = state.copyWith(allowedOrientations: orientation);
void setShortcuts(MapEntry<VideoHotKeys, KeyCombination?> newEntry) {
final currentShortcuts = Map.fromEntries(state.hotKeys.entries);
currentShortcuts.update(
newEntry.key,
(value) => newEntry.value,
ifAbsent: () => newEntry.value,
);
currentShortcuts.removeWhere((key, value) => value == null);
state = state.copyWith(hotKeys: currentShortcuts);
}
void nextChapter() {
final chapters = ref.read(playBackModel)?.chapters ?? [];
final currentPosition = ref.read(videoPlayerProvider.select((value) => value.lastState?.position));
if (chapters.isNotEmpty && currentPosition != null) {
final currentChapter = chapters.lastWhereOrNull((element) => element.startPosition <= currentPosition);
if (currentChapter != null) {
final nextChapterIndex = chapters.indexOf(currentChapter) + 1;
if (nextChapterIndex < chapters.length) {
ref.read(videoPlayerProvider).seek(chapters[nextChapterIndex].startPosition);
} else {
ref.read(videoPlayerProvider).seek(currentChapter.startPosition);
}
}
}
}
void prevChapter() {
final chapters = ref.read(playBackModel)?.chapters ?? [];
final currentPosition = ref.read(videoPlayerProvider.select((value) => value.lastState?.position));
if (chapters.isNotEmpty && currentPosition != null) {
final currentChapter = chapters.lastWhereOrNull((element) => element.startPosition <= currentPosition);
if (currentChapter != null) {
final prevChapterIndex = chapters.indexOf(currentChapter) - 1;
if (prevChapterIndex >= 0) {
ref.read(videoPlayerProvider).seek(chapters[prevChapterIndex].startPosition);
} else {
ref.read(videoPlayerProvider).seek(currentChapter.startPosition);
}
}
}
}
}