mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
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:
parent
23385d8e62
commit
fa30e634b4
29 changed files with 1360 additions and 162 deletions
|
|
@ -3,17 +3,35 @@ import 'dart:developer';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:fladder/models/settings/key_combinations.dart';
|
||||
import 'package:fladder/util/custom_color_themes.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
part 'client_settings_model.freezed.dart';
|
||||
part 'client_settings_model.g.dart';
|
||||
|
||||
enum GlobalHotKeys {
|
||||
search,
|
||||
exit;
|
||||
|
||||
const GlobalHotKeys();
|
||||
|
||||
String label(BuildContext context) {
|
||||
return switch (this) {
|
||||
GlobalHotKeys.search => context.localized.search,
|
||||
GlobalHotKeys.exit => context.localized.exitFladderTitle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Freezed(copyWith: true)
|
||||
class ClientSettingsModel with _$ClientSettingsModel {
|
||||
const ClientSettingsModel._();
|
||||
|
||||
factory ClientSettingsModel({
|
||||
String? syncPath,
|
||||
@Default(Vector2(x: 0, y: 0)) Vector2 position,
|
||||
|
|
@ -39,10 +57,16 @@ class ClientSettingsModel with _$ClientSettingsModel {
|
|||
@Default(false) bool usePosterForLibrary,
|
||||
String? lastViewedUpdate,
|
||||
int? libraryPageSize,
|
||||
@Default({}) Map<GlobalHotKeys, KeyCombination?> shortcuts,
|
||||
}) = _ClientSettingsModel;
|
||||
|
||||
factory ClientSettingsModel.fromJson(Map<String, dynamic> json) => _$ClientSettingsModelFromJson(json);
|
||||
|
||||
Map<GlobalHotKeys, KeyCombination> get currentShortcuts =>
|
||||
_defaultGlobalHotKeys.map((key, value) => MapEntry(key, shortcuts[key] ?? value));
|
||||
|
||||
Map<GlobalHotKeys, KeyCombination> get defaultShortCuts => _defaultGlobalHotKeys;
|
||||
|
||||
Brightness statusBarBrightness(BuildContext context) {
|
||||
return switch (themeMode) {
|
||||
ThemeMode.dark => Brightness.light,
|
||||
|
|
@ -132,3 +156,12 @@ class Vector2 {
|
|||
|
||||
static Vector2 fromPosition(Offset windowPosition) => Vector2(x: windowPosition.dx, y: windowPosition.dy);
|
||||
}
|
||||
|
||||
Map<GlobalHotKeys, KeyCombination> get _defaultGlobalHotKeys => {
|
||||
for (var hotKey in GlobalHotKeys.values)
|
||||
hotKey: switch (hotKey) {
|
||||
GlobalHotKeys.search =>
|
||||
KeyCombination(key: LogicalKeyboardKey.keyK, modifier: LogicalKeyboardKey.controlLeft),
|
||||
GlobalHotKeys.exit => KeyCombination(key: LogicalKeyboardKey.keyQ, modifier: LogicalKeyboardKey.controlLeft),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ mixin _$ClientSettingsModel {
|
|||
bool get usePosterForLibrary => throw _privateConstructorUsedError;
|
||||
String? get lastViewedUpdate => throw _privateConstructorUsedError;
|
||||
int? get libraryPageSize => throw _privateConstructorUsedError;
|
||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this ClientSettingsModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
|
@ -86,7 +88,8 @@ abstract class $ClientSettingsModelCopyWith<$Res> {
|
|||
bool checkForUpdates,
|
||||
bool usePosterForLibrary,
|
||||
String? lastViewedUpdate,
|
||||
int? libraryPageSize});
|
||||
int? libraryPageSize,
|
||||
Map<GlobalHotKeys, KeyCombination?> shortcuts});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -128,6 +131,7 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
|||
Object? usePosterForLibrary = null,
|
||||
Object? lastViewedUpdate = freezed,
|
||||
Object? libraryPageSize = freezed,
|
||||
Object? shortcuts = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
syncPath: freezed == syncPath
|
||||
|
|
@ -226,6 +230,10 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
|||
? _value.libraryPageSize
|
||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
shortcuts: null == shortcuts
|
||||
? _value.shortcuts
|
||||
: shortcuts // ignore: cast_nullable_to_non_nullable
|
||||
as Map<GlobalHotKeys, KeyCombination?>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
|
@ -262,7 +270,8 @@ abstract class _$$ClientSettingsModelImplCopyWith<$Res>
|
|||
bool checkForUpdates,
|
||||
bool usePosterForLibrary,
|
||||
String? lastViewedUpdate,
|
||||
int? libraryPageSize});
|
||||
int? libraryPageSize,
|
||||
Map<GlobalHotKeys, KeyCombination?> shortcuts});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -302,6 +311,7 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
|||
Object? usePosterForLibrary = null,
|
||||
Object? lastViewedUpdate = freezed,
|
||||
Object? libraryPageSize = freezed,
|
||||
Object? shortcuts = null,
|
||||
}) {
|
||||
return _then(_$ClientSettingsModelImpl(
|
||||
syncPath: freezed == syncPath
|
||||
|
|
@ -400,6 +410,10 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
|||
? _value.libraryPageSize
|
||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
shortcuts: null == shortcuts
|
||||
? _value._shortcuts
|
||||
: shortcuts // ignore: cast_nullable_to_non_nullable
|
||||
as Map<GlobalHotKeys, KeyCombination?>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -432,8 +446,10 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
|||
this.checkForUpdates = true,
|
||||
this.usePosterForLibrary = false,
|
||||
this.lastViewedUpdate,
|
||||
this.libraryPageSize})
|
||||
: super._();
|
||||
this.libraryPageSize,
|
||||
final Map<GlobalHotKeys, KeyCombination?> shortcuts = const {}})
|
||||
: _shortcuts = shortcuts,
|
||||
super._();
|
||||
|
||||
factory _$ClientSettingsModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$ClientSettingsModelImplFromJson(json);
|
||||
|
|
@ -505,10 +521,18 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
|||
final String? lastViewedUpdate;
|
||||
@override
|
||||
final int? libraryPageSize;
|
||||
final Map<GlobalHotKeys, KeyCombination?> _shortcuts;
|
||||
@override
|
||||
@JsonKey()
|
||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts {
|
||||
if (_shortcuts is EqualUnmodifiableMapView) return _shortcuts;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_shortcuts);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundPosters: $backgroundPosters, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize)';
|
||||
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundPosters: $backgroundPosters, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize, shortcuts: $shortcuts)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -541,7 +565,8 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
|||
..add(DiagnosticsProperty('checkForUpdates', checkForUpdates))
|
||||
..add(DiagnosticsProperty('usePosterForLibrary', usePosterForLibrary))
|
||||
..add(DiagnosticsProperty('lastViewedUpdate', lastViewedUpdate))
|
||||
..add(DiagnosticsProperty('libraryPageSize', libraryPageSize));
|
||||
..add(DiagnosticsProperty('libraryPageSize', libraryPageSize))
|
||||
..add(DiagnosticsProperty('shortcuts', shortcuts));
|
||||
}
|
||||
|
||||
/// Create a copy of ClientSettingsModel
|
||||
|
|
@ -563,30 +588,32 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
|||
|
||||
abstract class _ClientSettingsModel extends ClientSettingsModel {
|
||||
factory _ClientSettingsModel(
|
||||
{final String? syncPath,
|
||||
final Vector2 position,
|
||||
final Vector2 size,
|
||||
final Duration? timeOut,
|
||||
final Duration? nextUpDateCutoff,
|
||||
final ThemeMode themeMode,
|
||||
final ColorThemes? themeColor,
|
||||
final bool amoledBlack,
|
||||
final bool blurPlaceHolders,
|
||||
final bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() final Locale? selectedLocale,
|
||||
final bool enableMediaKeys,
|
||||
final double posterSize,
|
||||
final bool pinchPosterZoom,
|
||||
final bool mouseDragSupport,
|
||||
final bool requireWifi,
|
||||
final bool showAllCollectionTypes,
|
||||
final int maxConcurrentDownloads,
|
||||
final DynamicSchemeVariant schemeVariant,
|
||||
final bool backgroundPosters,
|
||||
final bool checkForUpdates,
|
||||
final bool usePosterForLibrary,
|
||||
final String? lastViewedUpdate,
|
||||
final int? libraryPageSize}) = _$ClientSettingsModelImpl;
|
||||
{final String? syncPath,
|
||||
final Vector2 position,
|
||||
final Vector2 size,
|
||||
final Duration? timeOut,
|
||||
final Duration? nextUpDateCutoff,
|
||||
final ThemeMode themeMode,
|
||||
final ColorThemes? themeColor,
|
||||
final bool amoledBlack,
|
||||
final bool blurPlaceHolders,
|
||||
final bool blurUpcomingEpisodes,
|
||||
@LocaleConvert() final Locale? selectedLocale,
|
||||
final bool enableMediaKeys,
|
||||
final double posterSize,
|
||||
final bool pinchPosterZoom,
|
||||
final bool mouseDragSupport,
|
||||
final bool requireWifi,
|
||||
final bool showAllCollectionTypes,
|
||||
final int maxConcurrentDownloads,
|
||||
final DynamicSchemeVariant schemeVariant,
|
||||
final bool backgroundPosters,
|
||||
final bool checkForUpdates,
|
||||
final bool usePosterForLibrary,
|
||||
final String? lastViewedUpdate,
|
||||
final int? libraryPageSize,
|
||||
final Map<GlobalHotKeys, KeyCombination?> shortcuts}) =
|
||||
_$ClientSettingsModelImpl;
|
||||
_ClientSettingsModel._() : super._();
|
||||
|
||||
factory _ClientSettingsModel.fromJson(Map<String, dynamic> json) =
|
||||
|
|
@ -641,6 +668,8 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
|
|||
String? get lastViewedUpdate;
|
||||
@override
|
||||
int? get libraryPageSize;
|
||||
@override
|
||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts;
|
||||
|
||||
/// Create a copy of ClientSettingsModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@ _$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson(
|
|||
usePosterForLibrary: json['usePosterForLibrary'] as bool? ?? false,
|
||||
lastViewedUpdate: json['lastViewedUpdate'] as String?,
|
||||
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
||||
shortcuts: (json['shortcuts'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(
|
||||
$enumDecode(_$GlobalHotKeysEnumMap, k),
|
||||
e == null
|
||||
? null
|
||||
: KeyCombination.fromJson(e as Map<String, dynamic>)),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$ClientSettingsModelImplToJson(
|
||||
|
|
@ -75,6 +83,8 @@ Map<String, dynamic> _$$ClientSettingsModelImplToJson(
|
|||
'usePosterForLibrary': instance.usePosterForLibrary,
|
||||
'lastViewedUpdate': instance.lastViewedUpdate,
|
||||
'libraryPageSize': instance.libraryPageSize,
|
||||
'shortcuts': instance.shortcuts
|
||||
.map((k, e) => MapEntry(_$GlobalHotKeysEnumMap[k]!, e)),
|
||||
};
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
|
|
@ -112,3 +122,8 @@ const _$DynamicSchemeVariantEnumMap = {
|
|||
DynamicSchemeVariant.rainbow: 'rainbow',
|
||||
DynamicSchemeVariant.fruitSalad: 'fruitSalad',
|
||||
};
|
||||
|
||||
const _$GlobalHotKeysEnumMap = {
|
||||
GlobalHotKeys.search: 'search',
|
||||
GlobalHotKeys.exit: 'exit',
|
||||
};
|
||||
|
|
|
|||
70
lib/models/settings/key_combinations.dart
Normal file
70
lib/models/settings/key_combinations.dart
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:fladder/screens/settings/widgets/key_listener.dart';
|
||||
|
||||
part 'key_combinations.freezed.dart';
|
||||
part 'key_combinations.g.dart';
|
||||
|
||||
@Freezed(toJson: true, fromJson: true)
|
||||
class KeyCombination with _$KeyCombination {
|
||||
const KeyCombination._();
|
||||
|
||||
factory KeyCombination({
|
||||
@LogicalKeyboardSerializer() LogicalKeyboardKey? modifier,
|
||||
@LogicalKeyboardSerializer() required LogicalKeyboardKey key,
|
||||
}) = _KeyCombination;
|
||||
|
||||
factory KeyCombination.fromJson(Map<String, dynamic> json) => _$KeyCombinationFromJson(json);
|
||||
|
||||
@override
|
||||
bool operator ==(covariant other) {
|
||||
return other is KeyCombination && other.key.keyId == key.keyId && other.modifier?.keyId == modifier?.keyId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ modifier.hashCode;
|
||||
|
||||
String get label => [modifier?.label, key.label].nonNulls.join(" + ");
|
||||
|
||||
static final Set<LogicalKeyboardKey> shiftKeys = {
|
||||
LogicalKeyboardKey.shift,
|
||||
LogicalKeyboardKey.shiftLeft,
|
||||
LogicalKeyboardKey.shiftRight,
|
||||
};
|
||||
|
||||
static final altKeys = {
|
||||
LogicalKeyboardKey.alt,
|
||||
LogicalKeyboardKey.altRight,
|
||||
LogicalKeyboardKey.altLeft,
|
||||
};
|
||||
|
||||
static final ctrlKeys = {
|
||||
LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.controlLeft,
|
||||
LogicalKeyboardKey.controlRight,
|
||||
};
|
||||
|
||||
static final modifierKeys = {
|
||||
...shiftKeys,
|
||||
...altKeys,
|
||||
...ctrlKeys,
|
||||
};
|
||||
}
|
||||
|
||||
class LogicalKeyboardSerializer extends JsonConverter<LogicalKeyboardKey, String> {
|
||||
const LogicalKeyboardSerializer();
|
||||
|
||||
@override
|
||||
LogicalKeyboardKey fromJson(String json) {
|
||||
return LogicalKeyboardKey.findKeyByKeyId(int.parse(jsonDecode(json))) ?? LogicalKeyboardKey.abort;
|
||||
}
|
||||
|
||||
@override
|
||||
String toJson(LogicalKeyboardKey object) {
|
||||
return jsonEncode(object.keyId.toString());
|
||||
}
|
||||
}
|
||||
79
lib/models/settings/key_combinations.freezed.dart
Normal file
79
lib/models/settings/key_combinations.freezed.dart
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'key_combinations.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
KeyCombination _$KeyCombinationFromJson(Map<String, dynamic> json) {
|
||||
return _KeyCombination.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$KeyCombination {
|
||||
@LogicalKeyboardSerializer()
|
||||
LogicalKeyboardKey? get modifier => throw _privateConstructorUsedError;
|
||||
@LogicalKeyboardSerializer()
|
||||
LogicalKeyboardKey get key => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this KeyCombination to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$KeyCombinationImpl extends _KeyCombination {
|
||||
_$KeyCombinationImpl(
|
||||
{@LogicalKeyboardSerializer() this.modifier,
|
||||
@LogicalKeyboardSerializer() required this.key})
|
||||
: super._();
|
||||
|
||||
factory _$KeyCombinationImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$KeyCombinationImplFromJson(json);
|
||||
|
||||
@override
|
||||
@LogicalKeyboardSerializer()
|
||||
final LogicalKeyboardKey? modifier;
|
||||
@override
|
||||
@LogicalKeyboardSerializer()
|
||||
final LogicalKeyboardKey key;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'KeyCombination(modifier: $modifier, key: $key)';
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$KeyCombinationImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _KeyCombination extends KeyCombination {
|
||||
factory _KeyCombination(
|
||||
{@LogicalKeyboardSerializer() final LogicalKeyboardKey? modifier,
|
||||
@LogicalKeyboardSerializer() required final LogicalKeyboardKey key}) =
|
||||
_$KeyCombinationImpl;
|
||||
_KeyCombination._() : super._();
|
||||
|
||||
factory _KeyCombination.fromJson(Map<String, dynamic> json) =
|
||||
_$KeyCombinationImpl.fromJson;
|
||||
|
||||
@override
|
||||
@LogicalKeyboardSerializer()
|
||||
LogicalKeyboardKey? get modifier;
|
||||
@override
|
||||
@LogicalKeyboardSerializer()
|
||||
LogicalKeyboardKey get key;
|
||||
}
|
||||
34
lib/models/settings/key_combinations.g.dart
Normal file
34
lib/models/settings/key_combinations.g.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'key_combinations.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$KeyCombinationImpl _$$KeyCombinationImplFromJson(Map<String, dynamic> json) =>
|
||||
_$KeyCombinationImpl(
|
||||
modifier: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
||||
json['modifier'], const LogicalKeyboardSerializer().fromJson),
|
||||
key: const LogicalKeyboardSerializer().fromJson(json['key'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$KeyCombinationImplToJson(
|
||||
_$KeyCombinationImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'modifier': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
||||
instance.modifier, const LogicalKeyboardSerializer().toJson),
|
||||
'key': const LogicalKeyboardSerializer().toJson(instance.key),
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
Object? json,
|
||||
Value? Function(Json json) fromJson,
|
||||
) =>
|
||||
json == null ? null : fromJson(json as Json);
|
||||
|
||||
Json? _$JsonConverterToJson<Json, Value>(
|
||||
Value? value,
|
||||
Json? Function(Value value) toJson,
|
||||
) =>
|
||||
value == null ? null : toJson(value);
|
||||
|
|
@ -6,12 +6,49 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import 'package:fladder/models/items/media_segments_model.dart';
|
||||
import 'package:fladder/models/settings/key_combinations.dart';
|
||||
import 'package:fladder/util/bitrate_helper.dart';
|
||||
import 'package:fladder/util/localization_helper.dart';
|
||||
|
||||
part 'video_player_settings.freezed.dart';
|
||||
part 'video_player_settings.g.dart';
|
||||
|
||||
enum VideoHotKeys {
|
||||
playPause,
|
||||
seekForward,
|
||||
seekBack,
|
||||
mute,
|
||||
volumeUp,
|
||||
volumeDown,
|
||||
nextVideo,
|
||||
prevVideo,
|
||||
nextChapter,
|
||||
prevChapter,
|
||||
fullScreen,
|
||||
skipMediaSegment,
|
||||
exit;
|
||||
|
||||
const VideoHotKeys();
|
||||
|
||||
String label(BuildContext context) {
|
||||
return switch (this) {
|
||||
VideoHotKeys.playPause => context.localized.playPause,
|
||||
VideoHotKeys.seekForward => context.localized.seekForward,
|
||||
VideoHotKeys.seekBack => context.localized.seekBack,
|
||||
VideoHotKeys.mute => context.localized.mute,
|
||||
VideoHotKeys.volumeUp => context.localized.volumeUp,
|
||||
VideoHotKeys.volumeDown => context.localized.volumeDown,
|
||||
VideoHotKeys.nextVideo => context.localized.nextVideo,
|
||||
VideoHotKeys.prevVideo => context.localized.prevVideo,
|
||||
VideoHotKeys.nextChapter => context.localized.nextChapter,
|
||||
VideoHotKeys.prevChapter => context.localized.prevChapter,
|
||||
VideoHotKeys.fullScreen => context.localized.fullScreen,
|
||||
VideoHotKeys.skipMediaSegment => context.localized.skipMediaSegment,
|
||||
VideoHotKeys.exit => context.localized.exit,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Freezed(copyWith: true)
|
||||
class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
||||
const VideoPlayerSettingsModel._();
|
||||
|
|
@ -31,6 +68,7 @@ class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
|||
@Default(Bitrate.original) Bitrate maxInternetBitrate,
|
||||
String? audioDevice,
|
||||
@Default(defaultSegmentSkipValues) Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||
@Default({}) Map<VideoHotKeys, KeyCombination?> hotKeys,
|
||||
}) = _VideoPlayerSettingsModel;
|
||||
|
||||
double get volume => switch (defaultTargetPlatform) {
|
||||
|
|
@ -42,6 +80,11 @@ class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
|||
|
||||
PlayerOptions get wantedPlayer => playerOptions ?? PlayerOptions.platformDefaults;
|
||||
|
||||
Map<VideoHotKeys, KeyCombination> get currentShortcuts =>
|
||||
_defaultVideoHotKeys.map((key, value) => MapEntry(key, hotKeys[key] ?? value));
|
||||
|
||||
Map<VideoHotKeys, KeyCombination> get defaultShortCuts => _defaultVideoHotKeys;
|
||||
|
||||
bool playerSame(VideoPlayerSettingsModel other) {
|
||||
return other.hardwareAccel == hardwareAccel &&
|
||||
other.useLibass == useLibass &&
|
||||
|
|
@ -118,3 +161,22 @@ enum AutoNextType {
|
|||
AutoNextType.static => context.localized.autoNextOffStaticDesc,
|
||||
};
|
||||
}
|
||||
|
||||
Map<VideoHotKeys, KeyCombination> get _defaultVideoHotKeys => {
|
||||
for (var hotKey in VideoHotKeys.values)
|
||||
hotKey: switch (hotKey) {
|
||||
VideoHotKeys.playPause => KeyCombination(key: LogicalKeyboardKey.space),
|
||||
VideoHotKeys.seekForward => KeyCombination(key: LogicalKeyboardKey.arrowRight),
|
||||
VideoHotKeys.seekBack => KeyCombination(key: LogicalKeyboardKey.arrowLeft),
|
||||
VideoHotKeys.mute => KeyCombination(key: LogicalKeyboardKey.keyM),
|
||||
VideoHotKeys.volumeUp => KeyCombination(key: LogicalKeyboardKey.arrowUp),
|
||||
VideoHotKeys.volumeDown => KeyCombination(key: LogicalKeyboardKey.arrowDown),
|
||||
VideoHotKeys.prevVideo => KeyCombination(key: LogicalKeyboardKey.keyP, modifier: LogicalKeyboardKey.shift),
|
||||
VideoHotKeys.nextVideo => KeyCombination(key: LogicalKeyboardKey.keyN, modifier: LogicalKeyboardKey.shift),
|
||||
VideoHotKeys.nextChapter => KeyCombination(key: LogicalKeyboardKey.pageUp),
|
||||
VideoHotKeys.prevChapter => KeyCombination(key: LogicalKeyboardKey.pageDown),
|
||||
VideoHotKeys.fullScreen => KeyCombination(key: LogicalKeyboardKey.keyF),
|
||||
VideoHotKeys.skipMediaSegment => KeyCombination(key: LogicalKeyboardKey.keyS),
|
||||
VideoHotKeys.exit => KeyCombination(key: LogicalKeyboardKey.escape),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ mixin _$VideoPlayerSettingsModel {
|
|||
String? get audioDevice => throw _privateConstructorUsedError;
|
||||
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings =>
|
||||
throw _privateConstructorUsedError;
|
||||
Map<VideoHotKeys, KeyCombination?> get hotKeys =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this VideoPlayerSettingsModel to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
|
@ -68,7 +70,8 @@ abstract class $VideoPlayerSettingsModelCopyWith<$Res> {
|
|||
Bitrate maxHomeBitrate,
|
||||
Bitrate maxInternetBitrate,
|
||||
String? audioDevice,
|
||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings});
|
||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||
Map<VideoHotKeys, KeyCombination?> hotKeys});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -101,6 +104,7 @@ class _$VideoPlayerSettingsModelCopyWithImpl<$Res,
|
|||
Object? maxInternetBitrate = null,
|
||||
Object? audioDevice = freezed,
|
||||
Object? segmentSkipSettings = null,
|
||||
Object? hotKeys = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
screenBrightness: freezed == screenBrightness
|
||||
|
|
@ -159,6 +163,10 @@ class _$VideoPlayerSettingsModelCopyWithImpl<$Res,
|
|||
? _value.segmentSkipSettings
|
||||
: segmentSkipSettings // ignore: cast_nullable_to_non_nullable
|
||||
as Map<MediaSegmentType, SegmentSkip>,
|
||||
hotKeys: null == hotKeys
|
||||
? _value.hotKeys
|
||||
: hotKeys // ignore: cast_nullable_to_non_nullable
|
||||
as Map<VideoHotKeys, KeyCombination?>,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
|
@ -186,7 +194,8 @@ abstract class _$$VideoPlayerSettingsModelImplCopyWith<$Res>
|
|||
Bitrate maxHomeBitrate,
|
||||
Bitrate maxInternetBitrate,
|
||||
String? audioDevice,
|
||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings});
|
||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||
Map<VideoHotKeys, KeyCombination?> hotKeys});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -218,6 +227,7 @@ class __$$VideoPlayerSettingsModelImplCopyWithImpl<$Res>
|
|||
Object? maxInternetBitrate = null,
|
||||
Object? audioDevice = freezed,
|
||||
Object? segmentSkipSettings = null,
|
||||
Object? hotKeys = null,
|
||||
}) {
|
||||
return _then(_$VideoPlayerSettingsModelImpl(
|
||||
screenBrightness: freezed == screenBrightness
|
||||
|
|
@ -276,6 +286,10 @@ class __$$VideoPlayerSettingsModelImplCopyWithImpl<$Res>
|
|||
? _value._segmentSkipSettings
|
||||
: segmentSkipSettings // ignore: cast_nullable_to_non_nullable
|
||||
as Map<MediaSegmentType, SegmentSkip>,
|
||||
hotKeys: null == hotKeys
|
||||
? _value._hotKeys
|
||||
: hotKeys // ignore: cast_nullable_to_non_nullable
|
||||
as Map<VideoHotKeys, KeyCombination?>,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -299,9 +313,11 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
|
|||
this.maxInternetBitrate = Bitrate.original,
|
||||
this.audioDevice,
|
||||
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings =
|
||||
defaultSegmentSkipValues})
|
||||
defaultSegmentSkipValues,
|
||||
final Map<VideoHotKeys, KeyCombination?> hotKeys = const {}})
|
||||
: _allowedOrientations = allowedOrientations,
|
||||
_segmentSkipSettings = segmentSkipSettings,
|
||||
_hotKeys = hotKeys,
|
||||
super._();
|
||||
|
||||
factory _$VideoPlayerSettingsModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||
|
|
@ -361,9 +377,18 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
|
|||
return EqualUnmodifiableMapView(_segmentSkipSettings);
|
||||
}
|
||||
|
||||
final Map<VideoHotKeys, KeyCombination?> _hotKeys;
|
||||
@override
|
||||
@JsonKey()
|
||||
Map<VideoHotKeys, KeyCombination?> get hotKeys {
|
||||
if (_hotKeys is EqualUnmodifiableMapView) return _hotKeys;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_hotKeys);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||
return 'VideoPlayerSettingsModel(screenBrightness: $screenBrightness, videoFit: $videoFit, fillScreen: $fillScreen, hardwareAccel: $hardwareAccel, useLibass: $useLibass, bufferSize: $bufferSize, playerOptions: $playerOptions, internalVolume: $internalVolume, allowedOrientations: $allowedOrientations, nextVideoType: $nextVideoType, maxHomeBitrate: $maxHomeBitrate, maxInternetBitrate: $maxInternetBitrate, audioDevice: $audioDevice, segmentSkipSettings: $segmentSkipSettings)';
|
||||
return 'VideoPlayerSettingsModel(screenBrightness: $screenBrightness, videoFit: $videoFit, fillScreen: $fillScreen, hardwareAccel: $hardwareAccel, useLibass: $useLibass, bufferSize: $bufferSize, playerOptions: $playerOptions, internalVolume: $internalVolume, allowedOrientations: $allowedOrientations, nextVideoType: $nextVideoType, maxHomeBitrate: $maxHomeBitrate, maxInternetBitrate: $maxInternetBitrate, audioDevice: $audioDevice, segmentSkipSettings: $segmentSkipSettings, hotKeys: $hotKeys)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -384,7 +409,8 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
|
|||
..add(DiagnosticsProperty('maxHomeBitrate', maxHomeBitrate))
|
||||
..add(DiagnosticsProperty('maxInternetBitrate', maxInternetBitrate))
|
||||
..add(DiagnosticsProperty('audioDevice', audioDevice))
|
||||
..add(DiagnosticsProperty('segmentSkipSettings', segmentSkipSettings));
|
||||
..add(DiagnosticsProperty('segmentSkipSettings', segmentSkipSettings))
|
||||
..add(DiagnosticsProperty('hotKeys', hotKeys));
|
||||
}
|
||||
|
||||
/// Create a copy of VideoPlayerSettingsModel
|
||||
|
|
@ -419,7 +445,8 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
|
|||
final Bitrate maxHomeBitrate,
|
||||
final Bitrate maxInternetBitrate,
|
||||
final String? audioDevice,
|
||||
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings}) =
|
||||
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||
final Map<VideoHotKeys, KeyCombination?> hotKeys}) =
|
||||
_$VideoPlayerSettingsModelImpl;
|
||||
_VideoPlayerSettingsModel._() : super._();
|
||||
|
||||
|
|
@ -454,6 +481,8 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
|
|||
String? get audioDevice;
|
||||
@override
|
||||
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings;
|
||||
@override
|
||||
Map<VideoHotKeys, KeyCombination?> get hotKeys;
|
||||
|
||||
/// Create a copy of VideoPlayerSettingsModel
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,14 @@ _$VideoPlayerSettingsModelImpl _$$VideoPlayerSettingsModelImplFromJson(
|
|||
$enumDecode(_$SegmentSkipEnumMap, e)),
|
||||
) ??
|
||||
defaultSegmentSkipValues,
|
||||
hotKeys: (json['hotKeys'] as Map<String, dynamic>?)?.map(
|
||||
(k, e) => MapEntry(
|
||||
$enumDecode(_$VideoHotKeysEnumMap, k),
|
||||
e == null
|
||||
? null
|
||||
: KeyCombination.fromJson(e as Map<String, dynamic>)),
|
||||
) ??
|
||||
const {},
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$VideoPlayerSettingsModelImplToJson(
|
||||
|
|
@ -60,6 +68,8 @@ Map<String, dynamic> _$$VideoPlayerSettingsModelImplToJson(
|
|||
'audioDevice': instance.audioDevice,
|
||||
'segmentSkipSettings': instance.segmentSkipSettings.map((k, e) =>
|
||||
MapEntry(_$MediaSegmentTypeEnumMap[k]!, _$SegmentSkipEnumMap[e]!)),
|
||||
'hotKeys': instance.hotKeys
|
||||
.map((k, e) => MapEntry(_$VideoHotKeysEnumMap[k]!, e)),
|
||||
};
|
||||
|
||||
const _$BoxFitEnumMap = {
|
||||
|
|
@ -123,3 +133,19 @@ const _$MediaSegmentTypeEnumMap = {
|
|||
MediaSegmentType.outro: 'outro',
|
||||
MediaSegmentType.intro: 'intro',
|
||||
};
|
||||
|
||||
const _$VideoHotKeysEnumMap = {
|
||||
VideoHotKeys.playPause: 'playPause',
|
||||
VideoHotKeys.seekForward: 'seekForward',
|
||||
VideoHotKeys.seekBack: 'seekBack',
|
||||
VideoHotKeys.mute: 'mute',
|
||||
VideoHotKeys.volumeUp: 'volumeUp',
|
||||
VideoHotKeys.volumeDown: 'volumeDown',
|
||||
VideoHotKeys.nextVideo: 'nextVideo',
|
||||
VideoHotKeys.prevVideo: 'prevVideo',
|
||||
VideoHotKeys.nextChapter: 'nextChapter',
|
||||
VideoHotKeys.prevChapter: 'prevChapter',
|
||||
VideoHotKeys.fullScreen: 'fullScreen',
|
||||
VideoHotKeys.skipMediaSegment: 'skipMediaSegment',
|
||||
VideoHotKeys.exit: 'exit',
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue