video: custom playback buttons

This commit is contained in:
Thibault Deckers 2024-10-30 11:40:07 +01:00
parent 9e64da8940
commit e0b3f92b65
12 changed files with 76 additions and 56 deletions

View file

@ -272,10 +272,7 @@
"settingsVideoEnablePip": "Picture-in-picture", "settingsVideoEnablePip": "Picture-in-picture",
"videoControlsPlay": "Play",
"videoControlsPlaySeek": "Play & seek backward/forward",
"videoControlsPlayOutside": "Open with other player", "videoControlsPlayOutside": "Open with other player",
"videoControlsNone": "None",
"videoLoopModeNever": "Never", "videoLoopModeNever": "Never",
"videoLoopModeShortOnly": "Short videos only", "videoLoopModeShortOnly": "Short videos only",

View file

@ -133,7 +133,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings
viewerGestureSideTapNext = false; viewerGestureSideTapNext = false;
viewerUseCutout = true; viewerUseCutout = true;
videoBackgroundMode = VideoBackgroundMode.disabled; videoBackgroundMode = VideoBackgroundMode.disabled;
videoControls = VideoControls.none; videoControlActions = [];
videoGestureDoubleTapTogglePlay = false; videoGestureDoubleTapTogglePlay = false;
videoGestureSideDoubleTapSeek = false; videoGestureSideDoubleTapSeek = false;
enableBin = false; enableBin = false;
@ -459,7 +459,6 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings
case SettingKeys.videoBackgroundModeKey: case SettingKeys.videoBackgroundModeKey:
case SettingKeys.videoLoopModeKey: case SettingKeys.videoLoopModeKey:
case SettingKeys.videoResumptionModeKey: case SettingKeys.videoResumptionModeKey:
case SettingKeys.videoControlsKey:
case SettingKeys.subtitleTextAlignmentKey: case SettingKeys.subtitleTextAlignmentKey:
case SettingKeys.subtitleTextPositionKey: case SettingKeys.subtitleTextPositionKey:
case SettingKeys.tagEditorExpandedSectionKey: case SettingKeys.tagEditorExpandedSectionKey:
@ -490,6 +489,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings
case SettingKeys.collectionBrowsingQuickActionsKey: case SettingKeys.collectionBrowsingQuickActionsKey:
case SettingKeys.collectionSelectionQuickActionsKey: case SettingKeys.collectionSelectionQuickActionsKey:
case SettingKeys.viewerQuickActionsKey: case SettingKeys.viewerQuickActionsKey:
case SettingKeys.videoControlActionsKey:
case SettingKeys.screenSaverCollectionFiltersKey: case SettingKeys.screenSaverCollectionFiltersKey:
if (newValue is List) { if (newValue is List) {
store.setStringList(key, newValue.cast<String>()); store.setStringList(key, newValue.cast<String>());

View file

@ -47,7 +47,8 @@ extension ExtraEntryActionView on EntryAction {
EntryAction.videoShowNextFrame => l10n.videoActionShowNextFrame, EntryAction.videoShowNextFrame => l10n.videoActionShowNextFrame,
// external // external
EntryAction.edit => l10n.entryActionEdit, EntryAction.edit => l10n.entryActionEdit,
EntryAction.open || EntryAction.openVideo => l10n.entryActionOpen, EntryAction.open => l10n.entryActionOpen,
EntryAction.openVideo => l10n.videoControlsPlayOutside,
EntryAction.openMap => l10n.entryActionOpenMap, EntryAction.openMap => l10n.entryActionOpenMap,
EntryAction.setAs => l10n.entryActionSetAs, EntryAction.setAs => l10n.entryActionSetAs,
EntryAction.cast => l10n.entryActionCast, EntryAction.cast => l10n.entryActionCast,

View file

@ -174,18 +174,6 @@ extension ExtraVideoBackgroundModeView on VideoBackgroundMode {
} }
} }
extension ExtraVideoControlsView on VideoControls {
String getName(BuildContext context) {
final l10n = context.l10n;
return switch (this) {
VideoControls.play => l10n.videoControlsPlay,
VideoControls.playSeek => l10n.videoControlsPlaySeek,
VideoControls.playOutside => l10n.videoControlsPlayOutside,
VideoControls.none => l10n.videoControlsNone,
};
}
}
extension ExtraVideoLoopModeView on VideoLoopMode { extension ExtraVideoLoopModeView on VideoLoopMode {
String getName(BuildContext context) { String getName(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;

View file

@ -0,0 +1,58 @@
import 'package:aves/model/settings/settings.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart';
class VideoControlButtonsPage extends StatefulWidget {
static const routeName = '/settings/video/control_buttons';
const VideoControlButtonsPage({super.key});
@override
State<VideoControlButtonsPage> createState() => _VideoControlButtonsPageState();
}
class _VideoControlButtonsPageState extends State<VideoControlButtonsPage> {
late final Set<EntryAction> _selectedActions;
static const _availableActions = [...EntryActions.videoPlayback, EntryAction.openVideo];
@override
void initState() {
super.initState();
_selectedActions = settings.videoControlActions.toSet();
}
@override
Widget build(BuildContext context) {
return AvesScaffold(
appBar: AppBar(
automaticallyImplyLeading: !settings.useTvLayout,
title: Text(context.l10n.settingsViewerOverlayPageTitle),
),
body: SafeArea(
child: PopScope(
canPop: true,
onPopInvokedWithResult: (didPop, result) => settings.videoControlActions = _availableActions.where(_selectedActions.contains).toList(),
child: ListView(
children: _availableActions.map((action) {
return SwitchListTile(
value: _selectedActions.contains(action),
onChanged: (v) => setState(() {
if (v) {
_selectedActions.add(action);
} else {
_selectedActions.remove(action);
}
}),
title: Text(action.getText(context)),
);
}).toList(),
),
),
),
);
}
}

View file

@ -1,9 +1,8 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/settings/common/tiles.dart'; import 'package:aves/widgets/settings/common/tiles.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves/widgets/settings/video/control_actions.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class VideoControlsPage extends StatelessWidget { class VideoControlsPage extends StatelessWidget {
@ -20,12 +19,10 @@ class VideoControlsPage extends StatelessWidget {
body: SafeArea( body: SafeArea(
child: ListView( child: ListView(
children: [ children: [
SettingsSelectionListTile<VideoControls>( SettingsSubPageTile(
values: VideoControls.values, title: context.l10n.settingsVideoButtonsTile,
getName: (context, v) => v.getName(context), routeName: VideoControlButtonsPage.routeName,
selector: (context, s) => s.videoControls, builder: (context) => const VideoControlButtonsPage(),
onSelection: (v) => settings.videoControls = v,
tileTitle: context.l10n.settingsVideoButtonsTile,
), ),
SettingsSwitchListTile( SettingsSwitchListTile(
selector: (context, s) => s.videoGestureDoubleTapTogglePlay, selector: (context, s) => s.videoGestureDoubleTapTogglePlay,

View file

@ -28,11 +28,9 @@ class VideoControlRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Selector<Settings, VideoControls>( return Selector<Settings, List<EntryAction>>(
selector: (context, s) => s.videoControls, selector: (context, s) => s.videoControlActions,
builder: (context, videoControls, child) { builder: (context, actions, child) {
final actions = _toActions(videoControls);
if (actions.isEmpty) { if (actions.isEmpty) {
return const SizedBox(); return const SizedBox();
} }
@ -65,23 +63,6 @@ class VideoControlRow extends StatelessWidget {
); );
} }
List<EntryAction> _toActions(VideoControls videoControls) {
switch (videoControls) {
case VideoControls.play:
return [EntryAction.videoTogglePlay];
case VideoControls.playSeek:
return [
EntryAction.videoReplay10,
EntryAction.videoTogglePlay,
EntryAction.videoSkip10,
];
case VideoControls.playOutside:
return [EntryAction.openVideo];
case VideoControls.none:
return [];
}
}
Widget _buildOverlayButton( Widget _buildOverlayButton(
BuildContext context, BuildContext context,
EntryAction action, { EntryAction action, {

View file

@ -117,8 +117,6 @@ class EntryActions {
EntryAction.videoToggleMute, EntryAction.videoToggleMute,
EntryAction.videoSetSpeed, EntryAction.videoSetSpeed,
EntryAction.videoABRepeat, EntryAction.videoABRepeat,
EntryAction.videoShowPreviousFrame,
EntryAction.videoShowNextFrame,
EntryAction.videoSelectStreams, EntryAction.videoSelectStreams,
EntryAction.videoSettings, EntryAction.videoSettings,
EntryAction.lockViewer, EntryAction.lockViewer,
@ -126,7 +124,9 @@ class EntryActions {
static const videoPlayback = [ static const videoPlayback = [
EntryAction.videoReplay10, EntryAction.videoReplay10,
EntryAction.videoShowPreviousFrame,
EntryAction.videoTogglePlay, EntryAction.videoTogglePlay,
EntryAction.videoShowNextFrame,
EntryAction.videoSkip10, EntryAction.videoSkip10,
]; ];

View file

@ -36,8 +36,6 @@ enum VideoAutoPlayMode { disabled, playMuted, playWithSound }
enum VideoBackgroundMode { disabled, pip } enum VideoBackgroundMode { disabled, pip }
enum VideoControls { play, playSeek, playOutside, none }
enum VideoLoopMode { never, shortOnly, always } enum VideoLoopMode { never, shortOnly, always }
enum VideoResumptionMode { never, ask, always } enum VideoResumptionMode { never, ask, always }

View file

@ -109,7 +109,7 @@ class SettingKeys {
static const videoAutoPlayModeKey = 'video_auto_play_mode'; static const videoAutoPlayModeKey = 'video_auto_play_mode';
static const videoLoopModeKey = 'video_loop'; static const videoLoopModeKey = 'video_loop';
static const videoResumptionModeKey = 'video_resumption_mode'; static const videoResumptionModeKey = 'video_resumption_mode';
static const videoControlsKey = 'video_controls'; static const videoControlActionsKey = 'video_control_actions';
static const videoGestureDoubleTapTogglePlayKey = 'video_gesture_double_tap_toggle_play'; static const videoGestureDoubleTapTogglePlayKey = 'video_gesture_double_tap_toggle_play';
static const videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip'; static const videoGestureSideDoubleTapSeekKey = 'video_gesture_side_double_tap_skip';
static const videoGestureVerticalDragBrightnessVolumeKey = 'video_gesture_vertical_drag_brightness_volume'; static const videoGestureVerticalDragBrightnessVolumeKey = 'video_gesture_vertical_drag_brightness_volume';

View file

@ -11,7 +11,7 @@ class SettingsDefaults {
static const videoLoopMode = VideoLoopMode.shortOnly; static const videoLoopMode = VideoLoopMode.shortOnly;
static const videoResumptionMode = VideoResumptionMode.ask; static const videoResumptionMode = VideoResumptionMode.ask;
static const videoShowRawTimedText = false; static const videoShowRawTimedText = false;
static const videoControls = VideoControls.play; static const videoControlActions = [EntryAction.videoTogglePlay];
static const videoGestureDoubleTapTogglePlay = false; static const videoGestureDoubleTapTogglePlay = false;
static const videoGestureSideDoubleTapSeek = true; static const videoGestureSideDoubleTapSeek = true;
static const videoGestureVerticalDragBrightnessVolume = false; static const videoGestureVerticalDragBrightnessVolume = false;

View file

@ -22,9 +22,9 @@ mixin VideoSettings on SettingsAccess {
set videoResumptionMode(VideoResumptionMode newValue) => set(SettingKeys.videoResumptionModeKey, newValue.toString()); set videoResumptionMode(VideoResumptionMode newValue) => set(SettingKeys.videoResumptionModeKey, newValue.toString());
VideoControls get videoControls => getEnumOrDefault(SettingKeys.videoControlsKey, SettingsDefaults.videoControls, VideoControls.values); List<EntryAction> get videoControlActions => getEnumListOrDefault(SettingKeys.videoControlActionsKey, SettingsDefaults.videoControlActions, EntryAction.values);
set videoControls(VideoControls newValue) => set(SettingKeys.videoControlsKey, newValue.toString()); set videoControlActions(List<EntryAction> newValue) => set(SettingKeys.videoControlActionsKey, newValue.map((v) => v.toString()).toList());
bool get videoGestureDoubleTapTogglePlay => getBool(SettingKeys.videoGestureDoubleTapTogglePlayKey) ?? SettingsDefaults.videoGestureDoubleTapTogglePlay; bool get videoGestureDoubleTapTogglePlay => getBool(SettingKeys.videoGestureDoubleTapTogglePlayKey) ?? SettingsDefaults.videoGestureDoubleTapTogglePlay;