mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-13 09:20:31 -07:00
chore: Shortcuts improvements (#446)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
d0d6a2ffa6
commit
70e346b8a2
15 changed files with 481 additions and 198 deletions
|
|
@ -79,7 +79,7 @@ class ClientSettingsModel with _$ClientSettingsModel {
|
||||||
@Default(false) bool usePosterForLibrary,
|
@Default(false) bool usePosterForLibrary,
|
||||||
String? lastViewedUpdate,
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize,
|
int? libraryPageSize,
|
||||||
@Default({}) Map<GlobalHotKeys, KeyCombination?> shortcuts,
|
@Default({}) Map<GlobalHotKeys, KeyCombination> shortcuts,
|
||||||
}) = _ClientSettingsModel;
|
}) = _ClientSettingsModel;
|
||||||
|
|
||||||
factory ClientSettingsModel.fromJson(Map<String, dynamic> json) => _$ClientSettingsModelFromJson(json);
|
factory ClientSettingsModel.fromJson(Map<String, dynamic> json) => _$ClientSettingsModelFromJson(json);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ mixin _$ClientSettingsModel {
|
||||||
bool get usePosterForLibrary => throw _privateConstructorUsedError;
|
bool get usePosterForLibrary => throw _privateConstructorUsedError;
|
||||||
String? get lastViewedUpdate => throw _privateConstructorUsedError;
|
String? get lastViewedUpdate => throw _privateConstructorUsedError;
|
||||||
int? get libraryPageSize => throw _privateConstructorUsedError;
|
int? get libraryPageSize => throw _privateConstructorUsedError;
|
||||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts =>
|
Map<GlobalHotKeys, KeyCombination> get shortcuts =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this ClientSettingsModel to a JSON map.
|
/// Serializes this ClientSettingsModel to a JSON map.
|
||||||
|
|
@ -89,7 +89,7 @@ abstract class $ClientSettingsModelCopyWith<$Res> {
|
||||||
bool usePosterForLibrary,
|
bool usePosterForLibrary,
|
||||||
String? lastViewedUpdate,
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize,
|
int? libraryPageSize,
|
||||||
Map<GlobalHotKeys, KeyCombination?> shortcuts});
|
Map<GlobalHotKeys, KeyCombination> shortcuts});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -233,7 +233,7 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
||||||
shortcuts: null == shortcuts
|
shortcuts: null == shortcuts
|
||||||
? _value.shortcuts
|
? _value.shortcuts
|
||||||
: shortcuts // ignore: cast_nullable_to_non_nullable
|
: shortcuts // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<GlobalHotKeys, KeyCombination?>,
|
as Map<GlobalHotKeys, KeyCombination>,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +271,7 @@ abstract class _$$ClientSettingsModelImplCopyWith<$Res>
|
||||||
bool usePosterForLibrary,
|
bool usePosterForLibrary,
|
||||||
String? lastViewedUpdate,
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize,
|
int? libraryPageSize,
|
||||||
Map<GlobalHotKeys, KeyCombination?> shortcuts});
|
Map<GlobalHotKeys, KeyCombination> shortcuts});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -413,7 +413,7 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
||||||
shortcuts: null == shortcuts
|
shortcuts: null == shortcuts
|
||||||
? _value._shortcuts
|
? _value._shortcuts
|
||||||
: shortcuts // ignore: cast_nullable_to_non_nullable
|
: shortcuts // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<GlobalHotKeys, KeyCombination?>,
|
as Map<GlobalHotKeys, KeyCombination>,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -447,7 +447,7 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
||||||
this.usePosterForLibrary = false,
|
this.usePosterForLibrary = false,
|
||||||
this.lastViewedUpdate,
|
this.lastViewedUpdate,
|
||||||
this.libraryPageSize,
|
this.libraryPageSize,
|
||||||
final Map<GlobalHotKeys, KeyCombination?> shortcuts = const {}})
|
final Map<GlobalHotKeys, KeyCombination> shortcuts = const {}})
|
||||||
: _shortcuts = shortcuts,
|
: _shortcuts = shortcuts,
|
||||||
super._();
|
super._();
|
||||||
|
|
||||||
|
|
@ -521,10 +521,10 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
||||||
final String? lastViewedUpdate;
|
final String? lastViewedUpdate;
|
||||||
@override
|
@override
|
||||||
final int? libraryPageSize;
|
final int? libraryPageSize;
|
||||||
final Map<GlobalHotKeys, KeyCombination?> _shortcuts;
|
final Map<GlobalHotKeys, KeyCombination> _shortcuts;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts {
|
Map<GlobalHotKeys, KeyCombination> get shortcuts {
|
||||||
if (_shortcuts is EqualUnmodifiableMapView) return _shortcuts;
|
if (_shortcuts is EqualUnmodifiableMapView) return _shortcuts;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableMapView(_shortcuts);
|
return EqualUnmodifiableMapView(_shortcuts);
|
||||||
|
|
@ -612,7 +612,7 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
|
||||||
final bool usePosterForLibrary,
|
final bool usePosterForLibrary,
|
||||||
final String? lastViewedUpdate,
|
final String? lastViewedUpdate,
|
||||||
final int? libraryPageSize,
|
final int? libraryPageSize,
|
||||||
final Map<GlobalHotKeys, KeyCombination?> shortcuts}) =
|
final Map<GlobalHotKeys, KeyCombination> shortcuts}) =
|
||||||
_$ClientSettingsModelImpl;
|
_$ClientSettingsModelImpl;
|
||||||
_ClientSettingsModel._() : super._();
|
_ClientSettingsModel._() : super._();
|
||||||
|
|
||||||
|
|
@ -669,7 +669,7 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
|
||||||
@override
|
@override
|
||||||
int? get libraryPageSize;
|
int? get libraryPageSize;
|
||||||
@override
|
@override
|
||||||
Map<GlobalHotKeys, KeyCombination?> get shortcuts;
|
Map<GlobalHotKeys, KeyCombination> get shortcuts;
|
||||||
|
|
||||||
/// Create a copy of ClientSettingsModel
|
/// Create a copy of ClientSettingsModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,8 @@ _$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson(
|
||||||
lastViewedUpdate: json['lastViewedUpdate'] as String?,
|
lastViewedUpdate: json['lastViewedUpdate'] as String?,
|
||||||
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
||||||
shortcuts: (json['shortcuts'] as Map<String, dynamic>?)?.map(
|
shortcuts: (json['shortcuts'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(
|
(k, e) => MapEntry($enumDecode(_$GlobalHotKeysEnumMap, k),
|
||||||
$enumDecode(_$GlobalHotKeysEnumMap, k),
|
KeyCombination.fromJson(e as Map<String, dynamic>)),
|
||||||
e == null
|
|
||||||
? null
|
|
||||||
: KeyCombination.fromJson(e as Map<String, dynamic>)),
|
|
||||||
) ??
|
) ??
|
||||||
const {},
|
const {},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,65 @@ import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.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.freezed.dart';
|
||||||
part 'key_combinations.g.dart';
|
part 'key_combinations.g.dart';
|
||||||
|
|
||||||
@Freezed(toJson: true, fromJson: true)
|
@Freezed(toJson: true, fromJson: true, copyWith: true)
|
||||||
class KeyCombination with _$KeyCombination {
|
class KeyCombination with _$KeyCombination {
|
||||||
const KeyCombination._();
|
const KeyCombination._();
|
||||||
|
|
||||||
factory KeyCombination({
|
factory KeyCombination({
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? key,
|
||||||
@LogicalKeyboardSerializer() LogicalKeyboardKey? modifier,
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? modifier,
|
||||||
@LogicalKeyboardSerializer() required LogicalKeyboardKey key,
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altKey,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altModifier,
|
||||||
}) = _KeyCombination;
|
}) = _KeyCombination;
|
||||||
|
|
||||||
factory KeyCombination.fromJson(Map<String, dynamic> json) => _$KeyCombinationFromJson(json);
|
factory KeyCombination.fromJson(Map<String, dynamic> json) => _$KeyCombinationFromJson(json);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant other) {
|
bool operator ==(covariant other) {
|
||||||
return other is KeyCombination && other.key.keyId == key.keyId && other.modifier?.keyId == modifier?.keyId;
|
return other is KeyCombination &&
|
||||||
|
other.key?.keyId == key?.keyId &&
|
||||||
|
other.modifier?.keyId == modifier?.keyId &&
|
||||||
|
other.altKey?.keyId == altKey?.keyId &&
|
||||||
|
other.altModifier?.keyId == altModifier?.keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool containsSameSet(KeyCombination other) {
|
||||||
|
return (key == other.key && modifier == other.modifier) || (altKey == other.key && altModifier == other.modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => key.hashCode ^ modifier.hashCode;
|
int get hashCode => key.hashCode ^ modifier.hashCode ^ altKey.hashCode ^ altModifier.hashCode;
|
||||||
|
|
||||||
String get label => [modifier?.label, key.label].nonNulls.join(" + ");
|
String get label => [modifier?.label, key?.label].nonNulls.join(" + ");
|
||||||
|
String get altLabel => [altModifier?.label, altKey?.label].nonNulls.join(" + ");
|
||||||
|
|
||||||
|
KeyCombination? get altSet => altKey != null
|
||||||
|
? copyWith(
|
||||||
|
key: altKey!,
|
||||||
|
modifier: altModifier,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
KeyCombination setKeys(
|
||||||
|
LogicalKeyboardKey? key, {
|
||||||
|
LogicalKeyboardKey? modifier,
|
||||||
|
bool alt = false,
|
||||||
|
}) {
|
||||||
|
return alt
|
||||||
|
? copyWith(
|
||||||
|
altKey: key,
|
||||||
|
altModifier: modifier,
|
||||||
|
)
|
||||||
|
: copyWith(
|
||||||
|
key: key ?? altKey,
|
||||||
|
modifier: key == null ? altModifier : modifier,
|
||||||
|
altKey: key == null ? null : altKey,
|
||||||
|
altModifier: key == null ? null : altModifier,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static final Set<LogicalKeyboardKey> shiftKeys = {
|
static final Set<LogicalKeyboardKey> shiftKeys = {
|
||||||
LogicalKeyboardKey.shift,
|
LogicalKeyboardKey.shift,
|
||||||
|
|
@ -68,3 +102,26 @@ class LogicalKeyboardSerializer extends JsonConverter<LogicalKeyboardKey, String
|
||||||
return jsonEncode(object.keyId.toString());
|
return jsonEncode(object.keyId.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LogicalKeyExtension on LogicalKeyboardKey {
|
||||||
|
String get label {
|
||||||
|
return switch (this) { LogicalKeyboardKey.space => "Space", _ => keyLabel };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension KeyMapExtension<T> on Map<T, KeyCombination> {
|
||||||
|
Map<T, KeyCombination> setOrRemove(MapEntry<T, KeyCombination> newEntry, Map<T, KeyCombination> defaultShortCuts) {
|
||||||
|
if (newEntry.value == defaultShortCuts[newEntry.key]) {
|
||||||
|
final currentShortcuts = Map.fromEntries(entries);
|
||||||
|
return (currentShortcuts..remove(newEntry.key));
|
||||||
|
} else {
|
||||||
|
final currentShortcuts = Map.fromEntries(entries);
|
||||||
|
currentShortcuts.update(
|
||||||
|
newEntry.key,
|
||||||
|
(value) => newEntry.value,
|
||||||
|
ifAbsent: () => newEntry.value,
|
||||||
|
);
|
||||||
|
return currentShortcuts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,38 +20,173 @@ KeyCombination _$KeyCombinationFromJson(Map<String, dynamic> json) {
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$KeyCombination {
|
mixin _$KeyCombination {
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
LogicalKeyboardKey? get key => throw _privateConstructorUsedError;
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
LogicalKeyboardKey? get modifier => throw _privateConstructorUsedError;
|
LogicalKeyboardKey? get modifier => throw _privateConstructorUsedError;
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
LogicalKeyboardKey get key => throw _privateConstructorUsedError;
|
LogicalKeyboardKey? get altKey => throw _privateConstructorUsedError;
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
LogicalKeyboardKey? get altModifier => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this KeyCombination to a JSON map.
|
/// Serializes this KeyCombination to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of KeyCombination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$KeyCombinationCopyWith<KeyCombination> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $KeyCombinationCopyWith<$Res> {
|
||||||
|
factory $KeyCombinationCopyWith(
|
||||||
|
KeyCombination value, $Res Function(KeyCombination) then) =
|
||||||
|
_$KeyCombinationCopyWithImpl<$Res, KeyCombination>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{@LogicalKeyboardSerializer() LogicalKeyboardKey? key,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? modifier,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altKey,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altModifier});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$KeyCombinationCopyWithImpl<$Res, $Val extends KeyCombination>
|
||||||
|
implements $KeyCombinationCopyWith<$Res> {
|
||||||
|
_$KeyCombinationCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of KeyCombination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? key = freezed,
|
||||||
|
Object? modifier = freezed,
|
||||||
|
Object? altKey = freezed,
|
||||||
|
Object? altModifier = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
key: freezed == key
|
||||||
|
? _value.key
|
||||||
|
: key // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
modifier: freezed == modifier
|
||||||
|
? _value.modifier
|
||||||
|
: modifier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
altKey: freezed == altKey
|
||||||
|
? _value.altKey
|
||||||
|
: altKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
altModifier: freezed == altModifier
|
||||||
|
? _value.altModifier
|
||||||
|
: altModifier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$KeyCombinationImplCopyWith<$Res>
|
||||||
|
implements $KeyCombinationCopyWith<$Res> {
|
||||||
|
factory _$$KeyCombinationImplCopyWith(_$KeyCombinationImpl value,
|
||||||
|
$Res Function(_$KeyCombinationImpl) then) =
|
||||||
|
__$$KeyCombinationImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{@LogicalKeyboardSerializer() LogicalKeyboardKey? key,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? modifier,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altKey,
|
||||||
|
@LogicalKeyboardSerializer() LogicalKeyboardKey? altModifier});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$KeyCombinationImplCopyWithImpl<$Res>
|
||||||
|
extends _$KeyCombinationCopyWithImpl<$Res, _$KeyCombinationImpl>
|
||||||
|
implements _$$KeyCombinationImplCopyWith<$Res> {
|
||||||
|
__$$KeyCombinationImplCopyWithImpl(
|
||||||
|
_$KeyCombinationImpl _value, $Res Function(_$KeyCombinationImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of KeyCombination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? key = freezed,
|
||||||
|
Object? modifier = freezed,
|
||||||
|
Object? altKey = freezed,
|
||||||
|
Object? altModifier = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$KeyCombinationImpl(
|
||||||
|
key: freezed == key
|
||||||
|
? _value.key
|
||||||
|
: key // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
modifier: freezed == modifier
|
||||||
|
? _value.modifier
|
||||||
|
: modifier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
altKey: freezed == altKey
|
||||||
|
? _value.altKey
|
||||||
|
: altKey // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
altModifier: freezed == altModifier
|
||||||
|
? _value.altModifier
|
||||||
|
: altModifier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as LogicalKeyboardKey?,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$KeyCombinationImpl extends _KeyCombination {
|
class _$KeyCombinationImpl extends _KeyCombination {
|
||||||
_$KeyCombinationImpl(
|
_$KeyCombinationImpl(
|
||||||
{@LogicalKeyboardSerializer() this.modifier,
|
{@LogicalKeyboardSerializer() this.key,
|
||||||
@LogicalKeyboardSerializer() required this.key})
|
@LogicalKeyboardSerializer() this.modifier,
|
||||||
|
@LogicalKeyboardSerializer() this.altKey,
|
||||||
|
@LogicalKeyboardSerializer() this.altModifier})
|
||||||
: super._();
|
: super._();
|
||||||
|
|
||||||
factory _$KeyCombinationImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$KeyCombinationImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$KeyCombinationImplFromJson(json);
|
_$$KeyCombinationImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
final LogicalKeyboardKey? key;
|
||||||
@override
|
@override
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
final LogicalKeyboardKey? modifier;
|
final LogicalKeyboardKey? modifier;
|
||||||
@override
|
@override
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
final LogicalKeyboardKey key;
|
final LogicalKeyboardKey? altKey;
|
||||||
|
@override
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
final LogicalKeyboardKey? altModifier;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'KeyCombination(modifier: $modifier, key: $key)';
|
return 'KeyCombination(key: $key, modifier: $modifier, altKey: $altKey, altModifier: $altModifier)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of KeyCombination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$KeyCombinationImplCopyWith<_$KeyCombinationImpl> get copyWith =>
|
||||||
|
__$$KeyCombinationImplCopyWithImpl<_$KeyCombinationImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$$KeyCombinationImplToJson(
|
return _$$KeyCombinationImplToJson(
|
||||||
|
|
@ -62,18 +197,33 @@ class _$KeyCombinationImpl extends _KeyCombination {
|
||||||
|
|
||||||
abstract class _KeyCombination extends KeyCombination {
|
abstract class _KeyCombination extends KeyCombination {
|
||||||
factory _KeyCombination(
|
factory _KeyCombination(
|
||||||
{@LogicalKeyboardSerializer() final LogicalKeyboardKey? modifier,
|
{@LogicalKeyboardSerializer() final LogicalKeyboardKey? key,
|
||||||
@LogicalKeyboardSerializer() required final LogicalKeyboardKey key}) =
|
@LogicalKeyboardSerializer() final LogicalKeyboardKey? modifier,
|
||||||
|
@LogicalKeyboardSerializer() final LogicalKeyboardKey? altKey,
|
||||||
|
@LogicalKeyboardSerializer() final LogicalKeyboardKey? altModifier}) =
|
||||||
_$KeyCombinationImpl;
|
_$KeyCombinationImpl;
|
||||||
_KeyCombination._() : super._();
|
_KeyCombination._() : super._();
|
||||||
|
|
||||||
factory _KeyCombination.fromJson(Map<String, dynamic> json) =
|
factory _KeyCombination.fromJson(Map<String, dynamic> json) =
|
||||||
_$KeyCombinationImpl.fromJson;
|
_$KeyCombinationImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
LogicalKeyboardKey? get key;
|
||||||
@override
|
@override
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
LogicalKeyboardKey? get modifier;
|
LogicalKeyboardKey? get modifier;
|
||||||
@override
|
@override
|
||||||
@LogicalKeyboardSerializer()
|
@LogicalKeyboardSerializer()
|
||||||
LogicalKeyboardKey get key;
|
LogicalKeyboardKey? get altKey;
|
||||||
|
@override
|
||||||
|
@LogicalKeyboardSerializer()
|
||||||
|
LogicalKeyboardKey? get altModifier;
|
||||||
|
|
||||||
|
/// Create a copy of KeyCombination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$KeyCombinationImplCopyWith<_$KeyCombinationImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,27 @@ part of 'key_combinations.dart';
|
||||||
|
|
||||||
_$KeyCombinationImpl _$$KeyCombinationImplFromJson(Map<String, dynamic> json) =>
|
_$KeyCombinationImpl _$$KeyCombinationImplFromJson(Map<String, dynamic> json) =>
|
||||||
_$KeyCombinationImpl(
|
_$KeyCombinationImpl(
|
||||||
|
key: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
||||||
|
json['key'], const LogicalKeyboardSerializer().fromJson),
|
||||||
modifier: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
modifier: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
||||||
json['modifier'], const LogicalKeyboardSerializer().fromJson),
|
json['modifier'], const LogicalKeyboardSerializer().fromJson),
|
||||||
key: const LogicalKeyboardSerializer().fromJson(json['key'] as String),
|
altKey: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
||||||
|
json['altKey'], const LogicalKeyboardSerializer().fromJson),
|
||||||
|
altModifier: _$JsonConverterFromJson<String, LogicalKeyboardKey>(
|
||||||
|
json['altModifier'], const LogicalKeyboardSerializer().fromJson),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$$KeyCombinationImplToJson(
|
Map<String, dynamic> _$$KeyCombinationImplToJson(
|
||||||
_$KeyCombinationImpl instance) =>
|
_$KeyCombinationImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
|
'key': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
||||||
|
instance.key, const LogicalKeyboardSerializer().toJson),
|
||||||
'modifier': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
'modifier': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
||||||
instance.modifier, const LogicalKeyboardSerializer().toJson),
|
instance.modifier, const LogicalKeyboardSerializer().toJson),
|
||||||
'key': const LogicalKeyboardSerializer().toJson(instance.key),
|
'altKey': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
||||||
|
instance.altKey, const LogicalKeyboardSerializer().toJson),
|
||||||
|
'altModifier': _$JsonConverterToJson<String, LogicalKeyboardKey>(
|
||||||
|
instance.altModifier, const LogicalKeyboardSerializer().toJson),
|
||||||
};
|
};
|
||||||
|
|
||||||
Value? _$JsonConverterFromJson<Json, Value>(
|
Value? _$JsonConverterFromJson<Json, Value>(
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
||||||
@Default(Bitrate.original) Bitrate maxInternetBitrate,
|
@Default(Bitrate.original) Bitrate maxInternetBitrate,
|
||||||
String? audioDevice,
|
String? audioDevice,
|
||||||
@Default(defaultSegmentSkipValues) Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
@Default(defaultSegmentSkipValues) Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||||
@Default({}) Map<VideoHotKeys, KeyCombination?> hotKeys,
|
@Default({}) Map<VideoHotKeys, KeyCombination> hotKeys,
|
||||||
}) = _VideoPlayerSettingsModel;
|
}) = _VideoPlayerSettingsModel;
|
||||||
|
|
||||||
double get volume => switch (defaultTargetPlatform) {
|
double get volume => switch (defaultTargetPlatform) {
|
||||||
|
|
@ -165,14 +165,25 @@ enum AutoNextType {
|
||||||
Map<VideoHotKeys, KeyCombination> get _defaultVideoHotKeys => {
|
Map<VideoHotKeys, KeyCombination> get _defaultVideoHotKeys => {
|
||||||
for (var hotKey in VideoHotKeys.values)
|
for (var hotKey in VideoHotKeys.values)
|
||||||
hotKey: switch (hotKey) {
|
hotKey: switch (hotKey) {
|
||||||
VideoHotKeys.playPause => KeyCombination(key: LogicalKeyboardKey.space),
|
VideoHotKeys.playPause => KeyCombination(
|
||||||
VideoHotKeys.seekForward => KeyCombination(key: LogicalKeyboardKey.arrowRight),
|
key: LogicalKeyboardKey.space,
|
||||||
VideoHotKeys.seekBack => KeyCombination(key: LogicalKeyboardKey.arrowLeft),
|
altKey: LogicalKeyboardKey.keyK,
|
||||||
|
),
|
||||||
|
VideoHotKeys.seekForward => KeyCombination(
|
||||||
|
key: LogicalKeyboardKey.arrowRight,
|
||||||
|
altKey: LogicalKeyboardKey.keyL,
|
||||||
|
),
|
||||||
|
VideoHotKeys.seekBack => KeyCombination(
|
||||||
|
key: LogicalKeyboardKey.arrowLeft,
|
||||||
|
altKey: LogicalKeyboardKey.keyJ,
|
||||||
|
),
|
||||||
VideoHotKeys.mute => KeyCombination(key: LogicalKeyboardKey.keyM),
|
VideoHotKeys.mute => KeyCombination(key: LogicalKeyboardKey.keyM),
|
||||||
VideoHotKeys.volumeUp => KeyCombination(key: LogicalKeyboardKey.arrowUp),
|
VideoHotKeys.volumeUp => KeyCombination(key: LogicalKeyboardKey.arrowUp),
|
||||||
VideoHotKeys.volumeDown => KeyCombination(key: LogicalKeyboardKey.arrowDown),
|
VideoHotKeys.volumeDown => KeyCombination(key: LogicalKeyboardKey.arrowDown),
|
||||||
VideoHotKeys.prevVideo => KeyCombination(key: LogicalKeyboardKey.keyP, modifier: LogicalKeyboardKey.shift),
|
VideoHotKeys.prevVideo =>
|
||||||
VideoHotKeys.nextVideo => KeyCombination(key: LogicalKeyboardKey.keyN, modifier: LogicalKeyboardKey.shift),
|
KeyCombination(key: LogicalKeyboardKey.keyP, modifier: LogicalKeyboardKey.shiftLeft),
|
||||||
|
VideoHotKeys.nextVideo =>
|
||||||
|
KeyCombination(key: LogicalKeyboardKey.keyN, modifier: LogicalKeyboardKey.shiftLeft),
|
||||||
VideoHotKeys.nextChapter => KeyCombination(key: LogicalKeyboardKey.pageUp),
|
VideoHotKeys.nextChapter => KeyCombination(key: LogicalKeyboardKey.pageUp),
|
||||||
VideoHotKeys.prevChapter => KeyCombination(key: LogicalKeyboardKey.pageDown),
|
VideoHotKeys.prevChapter => KeyCombination(key: LogicalKeyboardKey.pageDown),
|
||||||
VideoHotKeys.fullScreen => KeyCombination(key: LogicalKeyboardKey.keyF),
|
VideoHotKeys.fullScreen => KeyCombination(key: LogicalKeyboardKey.keyF),
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ mixin _$VideoPlayerSettingsModel {
|
||||||
String? get audioDevice => throw _privateConstructorUsedError;
|
String? get audioDevice => throw _privateConstructorUsedError;
|
||||||
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings =>
|
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
Map<VideoHotKeys, KeyCombination?> get hotKeys =>
|
Map<VideoHotKeys, KeyCombination> get hotKeys =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this VideoPlayerSettingsModel to a JSON map.
|
/// Serializes this VideoPlayerSettingsModel to a JSON map.
|
||||||
|
|
@ -71,7 +71,7 @@ abstract class $VideoPlayerSettingsModelCopyWith<$Res> {
|
||||||
Bitrate maxInternetBitrate,
|
Bitrate maxInternetBitrate,
|
||||||
String? audioDevice,
|
String? audioDevice,
|
||||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||||
Map<VideoHotKeys, KeyCombination?> hotKeys});
|
Map<VideoHotKeys, KeyCombination> hotKeys});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -166,7 +166,7 @@ class _$VideoPlayerSettingsModelCopyWithImpl<$Res,
|
||||||
hotKeys: null == hotKeys
|
hotKeys: null == hotKeys
|
||||||
? _value.hotKeys
|
? _value.hotKeys
|
||||||
: hotKeys // ignore: cast_nullable_to_non_nullable
|
: hotKeys // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<VideoHotKeys, KeyCombination?>,
|
as Map<VideoHotKeys, KeyCombination>,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ abstract class _$$VideoPlayerSettingsModelImplCopyWith<$Res>
|
||||||
Bitrate maxInternetBitrate,
|
Bitrate maxInternetBitrate,
|
||||||
String? audioDevice,
|
String? audioDevice,
|
||||||
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||||
Map<VideoHotKeys, KeyCombination?> hotKeys});
|
Map<VideoHotKeys, KeyCombination> hotKeys});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -289,7 +289,7 @@ class __$$VideoPlayerSettingsModelImplCopyWithImpl<$Res>
|
||||||
hotKeys: null == hotKeys
|
hotKeys: null == hotKeys
|
||||||
? _value._hotKeys
|
? _value._hotKeys
|
||||||
: hotKeys // ignore: cast_nullable_to_non_nullable
|
: hotKeys // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<VideoHotKeys, KeyCombination?>,
|
as Map<VideoHotKeys, KeyCombination>,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +314,7 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
|
||||||
this.audioDevice,
|
this.audioDevice,
|
||||||
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings =
|
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings =
|
||||||
defaultSegmentSkipValues,
|
defaultSegmentSkipValues,
|
||||||
final Map<VideoHotKeys, KeyCombination?> hotKeys = const {}})
|
final Map<VideoHotKeys, KeyCombination> hotKeys = const {}})
|
||||||
: _allowedOrientations = allowedOrientations,
|
: _allowedOrientations = allowedOrientations,
|
||||||
_segmentSkipSettings = segmentSkipSettings,
|
_segmentSkipSettings = segmentSkipSettings,
|
||||||
_hotKeys = hotKeys,
|
_hotKeys = hotKeys,
|
||||||
|
|
@ -377,10 +377,10 @@ class _$VideoPlayerSettingsModelImpl extends _VideoPlayerSettingsModel
|
||||||
return EqualUnmodifiableMapView(_segmentSkipSettings);
|
return EqualUnmodifiableMapView(_segmentSkipSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<VideoHotKeys, KeyCombination?> _hotKeys;
|
final Map<VideoHotKeys, KeyCombination> _hotKeys;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
Map<VideoHotKeys, KeyCombination?> get hotKeys {
|
Map<VideoHotKeys, KeyCombination> get hotKeys {
|
||||||
if (_hotKeys is EqualUnmodifiableMapView) return _hotKeys;
|
if (_hotKeys is EqualUnmodifiableMapView) return _hotKeys;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableMapView(_hotKeys);
|
return EqualUnmodifiableMapView(_hotKeys);
|
||||||
|
|
@ -446,7 +446,7 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
|
||||||
final Bitrate maxInternetBitrate,
|
final Bitrate maxInternetBitrate,
|
||||||
final String? audioDevice,
|
final String? audioDevice,
|
||||||
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
final Map<MediaSegmentType, SegmentSkip> segmentSkipSettings,
|
||||||
final Map<VideoHotKeys, KeyCombination?> hotKeys}) =
|
final Map<VideoHotKeys, KeyCombination> hotKeys}) =
|
||||||
_$VideoPlayerSettingsModelImpl;
|
_$VideoPlayerSettingsModelImpl;
|
||||||
_VideoPlayerSettingsModel._() : super._();
|
_VideoPlayerSettingsModel._() : super._();
|
||||||
|
|
||||||
|
|
@ -482,7 +482,7 @@ abstract class _VideoPlayerSettingsModel extends VideoPlayerSettingsModel {
|
||||||
@override
|
@override
|
||||||
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings;
|
Map<MediaSegmentType, SegmentSkip> get segmentSkipSettings;
|
||||||
@override
|
@override
|
||||||
Map<VideoHotKeys, KeyCombination?> get hotKeys;
|
Map<VideoHotKeys, KeyCombination> get hotKeys;
|
||||||
|
|
||||||
/// Create a copy of VideoPlayerSettingsModel
|
/// Create a copy of VideoPlayerSettingsModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,8 @@ _$VideoPlayerSettingsModelImpl _$$VideoPlayerSettingsModelImplFromJson(
|
||||||
) ??
|
) ??
|
||||||
defaultSegmentSkipValues,
|
defaultSegmentSkipValues,
|
||||||
hotKeys: (json['hotKeys'] as Map<String, dynamic>?)?.map(
|
hotKeys: (json['hotKeys'] as Map<String, dynamic>?)?.map(
|
||||||
(k, e) => MapEntry(
|
(k, e) => MapEntry($enumDecode(_$VideoHotKeysEnumMap, k),
|
||||||
$enumDecode(_$VideoHotKeysEnumMap, k),
|
KeyCombination.fromJson(e as Map<String, dynamic>)),
|
||||||
e == null
|
|
||||||
? null
|
|
||||||
: KeyCombination.fromJson(e as Map<String, dynamic>)),
|
|
||||||
) ??
|
) ??
|
||||||
const {},
|
const {},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -60,14 +60,6 @@ class ClientSettingsNotifier extends StateNotifier<ClientSettingsModel> {
|
||||||
|
|
||||||
void setRequireWifi(bool value) => state = state.copyWith(requireWifi: value);
|
void setRequireWifi(bool value) => state = state.copyWith(requireWifi: value);
|
||||||
|
|
||||||
void setShortcuts(MapEntry<GlobalHotKeys, KeyCombination?> mapEntry) {
|
void setShortcuts(MapEntry<GlobalHotKeys, KeyCombination> newEntry) =>
|
||||||
final newShortCuts = Map.fromEntries(state.shortcuts.entries);
|
state = state.copyWith(shortcuts: state.shortcuts.setOrRemove(newEntry, state.defaultShortCuts));
|
||||||
newShortCuts.update(
|
|
||||||
mapEntry.key,
|
|
||||||
(value) => mapEntry.value,
|
|
||||||
ifAbsent: () => mapEntry.value,
|
|
||||||
);
|
|
||||||
newShortCuts.removeWhere((key, value) => value == null);
|
|
||||||
state = state.copyWith(shortcuts: newShortCuts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,15 +70,8 @@ class VideoPlayerSettingsProviderNotifier extends StateNotifier<VideoPlayerSetti
|
||||||
void toggleOrientation(Set<DeviceOrientation>? orientation) =>
|
void toggleOrientation(Set<DeviceOrientation>? orientation) =>
|
||||||
state = state.copyWith(allowedOrientations: orientation);
|
state = state.copyWith(allowedOrientations: orientation);
|
||||||
|
|
||||||
void setShortcuts(MapEntry<VideoHotKeys, KeyCombination?> newEntry) {
|
void setShortcuts(MapEntry<VideoHotKeys, KeyCombination> newEntry) {
|
||||||
final currentShortcuts = Map.fromEntries(state.hotKeys.entries);
|
state = state.copyWith(hotKeys: state.hotKeys.setOrRemove(newEntry, state.defaultShortCuts));
|
||||||
currentShortcuts.update(
|
|
||||||
newEntry.key,
|
|
||||||
(value) => newEntry.value,
|
|
||||||
ifAbsent: () => newEntry.value,
|
|
||||||
);
|
|
||||||
currentShortcuts.removeWhere((key, value) => value == null);
|
|
||||||
state = state.copyWith(hotKeys: currentShortcuts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nextChapter() {
|
void nextChapter() {
|
||||||
|
|
|
||||||
|
|
@ -19,31 +19,26 @@ List<Widget> buildClientSettingsShortCuts(
|
||||||
SettingsLabelDivider(label: context.localized.shortCuts),
|
SettingsLabelDivider(label: context.localized.shortCuts),
|
||||||
[
|
[
|
||||||
...GlobalHotKeys.values.map(
|
...GlobalHotKeys.values.map(
|
||||||
(entry) {
|
(entry) => Padding(
|
||||||
final currentEntry = clientSettings.shortcuts[entry];
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
final defaultEntry = clientSettings.defaultShortCuts[entry]!;
|
child: Row(
|
||||||
return Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
Expanded(
|
||||||
child: Row(
|
child: Text(
|
||||||
children: [
|
entry.label(context),
|
||||||
Expanded(
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
child: Text(
|
|
||||||
entry.label(context),
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Flexible(
|
),
|
||||||
child: KeyCombinationWidget(
|
Flexible(
|
||||||
currentKey: currentEntry,
|
child: KeyCombinationWidget(
|
||||||
defaultKey: defaultEntry,
|
currentKey: clientSettings.shortcuts[entry],
|
||||||
onChanged: (value) =>
|
defaultKey: clientSettings.defaultShortCuts[entry]!,
|
||||||
ref.read(clientSettingsProvider.notifier).setShortcuts(MapEntry(entry, value)),
|
onChanged: (value) => ref.read(clientSettingsProvider.notifier).setShortcuts(MapEntry(entry, value)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -207,31 +207,27 @@ class _PlayerSettingsPageState extends ConsumerState<PlayerSettingsPage> {
|
||||||
),
|
),
|
||||||
if (AdaptiveLayout.inputDeviceOf(context) == InputDevice.pointer)
|
if (AdaptiveLayout.inputDeviceOf(context) == InputDevice.pointer)
|
||||||
...VideoHotKeys.values.map(
|
...VideoHotKeys.values.map(
|
||||||
(entry) {
|
(entry) => Padding(
|
||||||
final currentEntry = videoSettings.hotKeys[entry];
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
final defaultEntry = videoSettings.defaultShortCuts[entry]!;
|
child: Row(
|
||||||
return Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
Expanded(
|
||||||
child: Row(
|
child: Text(
|
||||||
children: [
|
entry.label(context),
|
||||||
Expanded(
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
child: Text(
|
|
||||||
entry.label(context),
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Flexible(
|
),
|
||||||
child: KeyCombinationWidget(
|
Flexible(
|
||||||
currentKey: currentEntry,
|
child: KeyCombinationWidget(
|
||||||
defaultKey: defaultEntry,
|
currentKey: videoSettings.hotKeys[entry],
|
||||||
onChanged: (value) =>
|
defaultKey: videoSettings.defaultShortCuts[entry]!,
|
||||||
ref.read(videoPlayerSettingsProvider.notifier).setShortcuts(MapEntry(entry, value)),
|
onChanged: (value) =>
|
||||||
),
|
ref.read(videoPlayerSettingsProvider.notifier).setShortcuts(MapEntry(entry, value)),
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,27 +8,100 @@ import 'package:fladder/models/settings/key_combinations.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
|
import 'package:fladder/theme.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
class KeyCombinationWidget extends ConsumerStatefulWidget {
|
// Only use for actively checking if a shortcut is being changed
|
||||||
|
bool changingShortCut = false;
|
||||||
|
|
||||||
|
class KeyCombinationWidget extends StatelessWidget {
|
||||||
final KeyCombination? currentKey;
|
final KeyCombination? currentKey;
|
||||||
final KeyCombination defaultKey;
|
final KeyCombination defaultKey;
|
||||||
final Function(KeyCombination? value) onChanged;
|
final Function(KeyCombination value) onChanged;
|
||||||
|
|
||||||
KeyCombinationWidget({required this.currentKey, required this.defaultKey, required this.onChanged, super.key});
|
const KeyCombinationWidget({
|
||||||
|
required this.currentKey,
|
||||||
|
required this.defaultKey,
|
||||||
|
required this.onChanged,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
KeyCombinationWidgetState createState() => KeyCombinationWidgetState();
|
Widget build(BuildContext context) {
|
||||||
|
final comboKey = currentKey ?? defaultKey;
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(minWidth: 50),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 6,
|
||||||
|
children: [
|
||||||
|
KeyListenerWidget(
|
||||||
|
currentKey: comboKey,
|
||||||
|
onChanged: (value) => onChanged(comboKey.setKeys(
|
||||||
|
value?.key,
|
||||||
|
modifier: value?.modifier,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
if (comboKey.key != null) ...[
|
||||||
|
const Opacity(opacity: 0.25, child: Text("alt")),
|
||||||
|
KeyListenerWidget(
|
||||||
|
currentKey: comboKey.altSet,
|
||||||
|
onChanged: (value) => onChanged(comboKey.setKeys(
|
||||||
|
value?.key,
|
||||||
|
modifier: value?.modifier,
|
||||||
|
alt: true,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: currentKey == defaultKey || currentKey == null ? null : () => onChanged(defaultKey),
|
||||||
|
iconSize: 24,
|
||||||
|
icon: const Icon(IconsaxPlusBold.broom),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KeyCombinationWidgetState extends ConsumerState<KeyCombinationWidget> {
|
class KeyListenerWidget extends ConsumerStatefulWidget {
|
||||||
|
final KeyCombination? currentKey;
|
||||||
|
final Function(KeyCombination? value) onChanged;
|
||||||
|
|
||||||
|
KeyListenerWidget({
|
||||||
|
required this.currentKey,
|
||||||
|
required this.onChanged,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
KeyListenerWidgetState createState() => KeyListenerWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class KeyListenerWidgetState extends ConsumerState<KeyListenerWidget> {
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
bool _isListening = false;
|
bool _isListening = false;
|
||||||
|
bool _showClearButton = false;
|
||||||
LogicalKeyboardKey? _pressedKey;
|
LogicalKeyboardKey? _pressedKey;
|
||||||
LogicalKeyboardKey? _pressedModifier;
|
LogicalKeyboardKey? _pressedModifier;
|
||||||
|
|
||||||
|
void setIsListening(bool value) {
|
||||||
|
changingShortCut = value;
|
||||||
|
_isListening = value;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
changingShortCut = false;
|
||||||
_isListening = false;
|
_isListening = false;
|
||||||
_pressedKey = null;
|
_pressedKey = null;
|
||||||
_pressedModifier = null;
|
_pressedModifier = null;
|
||||||
|
|
@ -36,8 +109,9 @@ class KeyCombinationWidgetState extends ConsumerState<KeyCombinationWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startListening() {
|
void _startListening() {
|
||||||
|
if (changingShortCut) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_isListening = true;
|
setIsListening(true);
|
||||||
_pressedKey = null;
|
_pressedKey = null;
|
||||||
_pressedModifier = null;
|
_pressedModifier = null;
|
||||||
});
|
});
|
||||||
|
|
@ -45,17 +119,13 @@ class KeyCombinationWidgetState extends ConsumerState<KeyCombinationWidget> {
|
||||||
|
|
||||||
void _stopListening() {
|
void _stopListening() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isListening = false;
|
setIsListening(false);
|
||||||
if (_pressedKey != null) {
|
if (_pressedKey != null) {
|
||||||
final newKeyComb = KeyCombination(
|
final newKeyComb = KeyCombination(
|
||||||
key: _pressedKey!,
|
key: _pressedKey!,
|
||||||
modifier: _pressedModifier,
|
modifier: _pressedModifier,
|
||||||
);
|
);
|
||||||
if (newKeyComb == widget.defaultKey) {
|
widget.onChanged(newKeyComb);
|
||||||
widget.onChanged(null);
|
|
||||||
} else {
|
|
||||||
widget.onChanged(newKeyComb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_pressedKey = null;
|
_pressedKey = null;
|
||||||
_pressedModifier = null;
|
_pressedModifier = null;
|
||||||
|
|
@ -76,7 +146,7 @@ class KeyCombinationWidgetState extends ConsumerState<KeyCombinationWidget> {
|
||||||
} else {
|
} else {
|
||||||
final currentHotKey = KeyCombination(key: event.logicalKey, modifier: _pressedModifier);
|
final currentHotKey = KeyCombination(key: event.logicalKey, modifier: _pressedModifier);
|
||||||
bool isExistingHotkey = activeHotKeys.any((element) {
|
bool isExistingHotkey = activeHotKeys.any((element) {
|
||||||
return element == currentHotKey && currentHotKey != (widget.currentKey ?? widget.defaultKey);
|
return element.containsSameSet(currentHotKey) && currentHotKey != widget.currentKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isExistingHotkey) {
|
if (!isExistingHotkey) {
|
||||||
|
|
@ -103,72 +173,78 @@ class KeyCombinationWidgetState extends ConsumerState<KeyCombinationWidget> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showClearButton(bool value) {
|
||||||
|
setState(() {
|
||||||
|
_showClearButton = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final currentModifier =
|
final currentModifier = _pressedModifier ?? (widget.currentKey?.modifier);
|
||||||
_pressedModifier ?? (widget.currentKey != null ? widget.currentKey?.modifier : widget.defaultKey.modifier);
|
final currentKey = _pressedKey ?? widget.currentKey?.key;
|
||||||
final currentKey = _pressedKey ?? (widget.currentKey?.key ?? widget.defaultKey.key);
|
final currentHotKey = currentKey == null ? null : KeyCombination(key: currentKey, modifier: currentModifier);
|
||||||
final currentHotKey = KeyCombination(key: currentKey, modifier: currentModifier);
|
return MouseRegion(
|
||||||
return Row(
|
onEnter: (event) => showClearButton(true),
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
onExit: (event) => showClearButton(false),
|
||||||
children: [
|
child: ClipRRect(
|
||||||
ConstrainedBox(
|
borderRadius: FladderTheme.smallShape.borderRadius,
|
||||||
constraints: const BoxConstraints(minWidth: 50),
|
child: InkWell(
|
||||||
child: InkWell(
|
onTap: _isListening ? _stopListening : _startListening,
|
||||||
onTap: _isListening ? null : _startListening,
|
onSecondaryTap: () {
|
||||||
child: Card(
|
setState(() {
|
||||||
color: Theme.of(context).colorScheme.primaryContainer,
|
setIsListening(false);
|
||||||
child: Padding(
|
widget.onChanged(null);
|
||||||
padding: const EdgeInsets.only(left: 12),
|
});
|
||||||
child: Row(
|
},
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
child: Container(
|
||||||
mainAxisSize: MainAxisSize.min,
|
color: Theme.of(context).colorScheme.primaryContainer,
|
||||||
spacing: 6,
|
child: AnimatedSize(
|
||||||
children: [
|
duration: const Duration(milliseconds: 125),
|
||||||
Text(currentHotKey.label),
|
child: Stack(
|
||||||
AnimatedSwitcher(
|
alignment: Alignment.center,
|
||||||
duration: const Duration(milliseconds: 250),
|
children: [
|
||||||
child: _isListening
|
Padding(
|
||||||
? KeyboardListener(
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
focusNode: focusNode,
|
child: Row(
|
||||||
autofocus: true,
|
spacing: 8,
|
||||||
onKeyEvent: _handleKeyEvent,
|
children: [
|
||||||
child: const Padding(
|
if (_showClearButton && currentHotKey != null)
|
||||||
padding: EdgeInsets.all(8.0),
|
GestureDetector(
|
||||||
child: SizedBox(
|
onTap: () {
|
||||||
height: 24,
|
setIsListening(false);
|
||||||
width: 24,
|
widget.onChanged(null);
|
||||||
child: Center(
|
},
|
||||||
child: CircularProgressIndicator(),
|
child: const Icon(
|
||||||
),
|
IconsaxPlusLinear.trash,
|
||||||
),
|
size: 17,
|
||||||
),
|
|
||||||
)
|
|
||||||
: IconButton(
|
|
||||||
onPressed: widget.currentKey == null
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
_pressedKey = null;
|
|
||||||
_pressedModifier = null;
|
|
||||||
widget.onChanged(null);
|
|
||||||
},
|
|
||||||
iconSize: 24,
|
|
||||||
icon: const Icon(IconsaxPlusBold.broom),
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
Text(
|
||||||
),
|
currentHotKey?.label ?? "+",
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (_isListening)
|
||||||
|
Positioned.fill(
|
||||||
|
child: KeyboardListener(
|
||||||
|
focusNode: focusNode,
|
||||||
|
autofocus: true,
|
||||||
|
onKeyEvent: _handleKeyEvent,
|
||||||
|
child: const Opacity(
|
||||||
|
opacity: 0.25,
|
||||||
|
child: LinearProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LogicalKeyExtension on LogicalKeyboardKey {
|
|
||||||
String get label {
|
|
||||||
return switch (this) { LogicalKeyboardKey.space => "Space", _ => keyLabel };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:fladder/models/settings/key_combinations.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
class InputHandler<T> extends StatefulWidget {
|
import 'package:fladder/models/settings/key_combinations.dart';
|
||||||
|
import 'package:fladder/screens/settings/widgets/key_listener.dart';
|
||||||
|
|
||||||
|
class InputHandler<T> extends ConsumerStatefulWidget {
|
||||||
final bool autoFocus;
|
final bool autoFocus;
|
||||||
final KeyEventResult Function(FocusNode node, KeyEvent event)? onKeyEvent;
|
final KeyEventResult Function(FocusNode node, KeyEvent event)? onKeyEvent;
|
||||||
final bool Function(T result)? keyMapResult;
|
final bool Function(T result)? keyMapResult;
|
||||||
|
|
@ -19,10 +22,10 @@ class InputHandler<T> extends StatefulWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<InputHandler> createState() => _InputHandlerState<T>();
|
ConsumerState<InputHandler> createState() => _InputHandlerState<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InputHandlerState<T> extends State<InputHandler<T>> {
|
class _InputHandlerState<T> extends ConsumerState<InputHandler<T>> {
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
|
|
||||||
LogicalKeyboardKey? pressedModifier;
|
LogicalKeyboardKey? pressedModifier;
|
||||||
|
|
@ -50,6 +53,8 @@ class _InputHandlerState<T> extends State<InputHandler<T>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEventResult _onKey(KeyEvent value) {
|
KeyEventResult _onKey(KeyEvent value) {
|
||||||
|
if (changingShortCut) return KeyEventResult.ignored;
|
||||||
|
|
||||||
final keyMap = widget.keyMap?.entries.nonNulls.toList() ?? [];
|
final keyMap = widget.keyMap?.entries.nonNulls.toList() ?? [];
|
||||||
if (value is KeyDownEvent || value is KeyRepeatEvent) {
|
if (value is KeyDownEvent || value is KeyRepeatEvent) {
|
||||||
if (KeyCombination.modifierKeys.contains(value.logicalKey)) {
|
if (KeyCombination.modifierKeys.contains(value.logicalKey)) {
|
||||||
|
|
@ -63,7 +68,11 @@ class _InputHandlerState<T> extends State<InputHandler<T>> {
|
||||||
bool isMainKeyPressed = value.logicalKey == keyCombination.key;
|
bool isMainKeyPressed = value.logicalKey == keyCombination.key;
|
||||||
bool isModifierKeyPressed = pressedModifier == keyCombination.modifier;
|
bool isModifierKeyPressed = pressedModifier == keyCombination.modifier;
|
||||||
|
|
||||||
if (isMainKeyPressed && isModifierKeyPressed) {
|
bool isAltKeyPressed = value.logicalKey == keyCombination.altKey;
|
||||||
|
|
||||||
|
bool isAltModifierKeyPressed = pressedModifier == keyCombination.altModifier;
|
||||||
|
|
||||||
|
if ((isMainKeyPressed && isModifierKeyPressed) || isAltKeyPressed && isAltModifierKeyPressed) {
|
||||||
if (widget.keyMapResult?.call(hotKey) ?? false) {
|
if (widget.keyMapResult?.call(hotKey) ?? false) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue