settings: video controls preview

This commit is contained in:
Thibault Deckers 2024-10-30 21:37:14 +01:00
parent da959874fc
commit 71efe696c1
4 changed files with 76 additions and 69 deletions

View file

@ -7,7 +7,8 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Map: create shortcut to custom region and filters - Map: create shortcut to custom region and filters
- Video: frame stepping forward/backward actions - Video: frame stepping forward/backward
- Video: custom playback buttons
- English (Shavian) translation (thanks Paranoid Android) - English (Shavian) translation (thanks Paranoid Android)
### Changed ### Changed

View file

@ -2,55 +2,67 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/view/view.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/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/settings/common/quick_actions/action_panel.dart';
import 'package:aves/widgets/viewer/overlay/video/controls.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class VideoControlButtonsPage extends StatefulWidget { class VideoControlButtonsPage extends StatelessWidget {
static const routeName = '/settings/video/control_buttons'; 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.openVideoPlayer]; static const _availableActions = [...EntryActions.videoPlayback, EntryAction.openVideoPlayer];
@override const VideoControlButtonsPage({super.key});
void initState() {
super.initState();
_selectedActions = settings.videoControlActions.toSet();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AvesScaffold( return AvesScaffold(
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: !settings.useTvLayout, automaticallyImplyLeading: !settings.useTvLayout,
title: Text(context.l10n.settingsViewerOverlayPageTitle), title: Text(context.l10n.settingsVideoControlsPageTitle),
), ),
body: SafeArea( body: SafeArea(
child: PopScope( child: Selector<Settings, List<EntryAction>>(
canPop: true, selector: (context, s) => s.videoControlActions,
onPopInvokedWithResult: (didPop, result) => settings.videoControlActions = _availableActions.where(_selectedActions.contains).toList(), builder: (context, selectedActionList, child) {
child: ListView( return Column(
children: _availableActions.map((action) { crossAxisAlignment: CrossAxisAlignment.stretch,
return SwitchListTile( children: [
value: _selectedActions.contains(action), ActionPanel(
onChanged: (v) => setState(() { child: Container(
if (v) { alignment: AlignmentDirectional.center,
_selectedActions.add(action); height: OverlayButton.getSize(context) + 48,
} else { child: selectedActionList.isNotEmpty
_selectedActions.remove(action); ? VideoControlRow(onActionSelected: (_) {})
} : Text(
}), context.l10n.settingsViewerQuickActionEmpty,
title: Text(action.getText(context)), style: Theme.of(context).textTheme.bodySmall,
); ),
}).toList(), ),
), ),
Expanded(
child: ListView(
children: _availableActions.map((action) {
return SwitchListTile(
value: selectedActionList.contains(action),
onChanged: (v) {
final selectedActionSet = settings.videoControlActions.toSet();
if (v) {
selectedActionSet.add(action);
} else {
selectedActionSet.remove(action);
}
settings.videoControlActions = _availableActions.where(selectedActionSet.contains).toList();
},
title: Text(action.getText(context)),
);
}).toList(),
),
),
],
);
},
), ),
), ),
); );

View file

@ -1,4 +1,3 @@
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/togglers/play.dart'; import 'package:aves/widgets/common/action_controls/togglers/play.dart';
@ -10,9 +9,9 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class VideoControlRow extends StatelessWidget { class VideoControlRow extends StatelessWidget {
final AvesEntry entry;
final AvesVideoController? controller; final AvesVideoController? controller;
final Animation<double> scale; final Animation<double> scale;
final bool canOpenVideoPlayer;
final Function(EntryAction value) onActionSelected; final Function(EntryAction value) onActionSelected;
static const double padding = 8; static const double padding = 8;
@ -20,9 +19,9 @@ class VideoControlRow extends StatelessWidget {
const VideoControlRow({ const VideoControlRow({
super.key, super.key,
required this.entry, this.controller,
required this.controller, this.scale = kAlwaysCompleteAnimation,
required this.scale, this.canOpenVideoPlayer = true,
required this.onActionSelected, required this.onActionSelected,
}); });
@ -31,29 +30,22 @@ class VideoControlRow extends StatelessWidget {
return Selector<Settings, List<EntryAction>>( return Selector<Settings, List<EntryAction>>(
selector: (context, s) => s.videoControlActions, selector: (context, s) => s.videoControlActions,
builder: (context, actions, child) { builder: (context, actions, child) {
if (actions.isEmpty) {
return const SizedBox();
}
if (actions.length == 1) {
final action = actions.first;
return Padding(
padding: const EdgeInsets.only(left: padding),
child: _buildOverlayButton(context, action, const BorderRadius.all(radius)),
);
}
return Padding( return Padding(
padding: const EdgeInsets.only(left: padding), padding: EdgeInsets.only(left: actions.isEmpty ? 0 : padding),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
textDirection: ViewerBottomOverlay.actionsDirection, textDirection: ViewerBottomOverlay.actionsDirection,
children: actions.map((action) { children: actions.map((action) {
var borderRadius = BorderRadius.zero; // null radius yields a circular button
if (action == actions.first) { BorderRadius? borderRadius;
borderRadius = const BorderRadius.horizontal(left: radius); if (actions.length > 1) {
} else if (action == actions.last) { // zero radius yields a square button
borderRadius = const BorderRadius.horizontal(right: radius); borderRadius = BorderRadius.zero;
if (action == actions.first) {
borderRadius = const BorderRadius.horizontal(left: radius);
} else if (action == actions.last) {
borderRadius = const BorderRadius.horizontal(right: radius);
}
} }
return _buildOverlayButton(context, action, borderRadius); return _buildOverlayButton(context, action, borderRadius);
}).toList(), }).toList(),
@ -66,7 +58,7 @@ class VideoControlRow extends StatelessWidget {
Widget _buildOverlayButton( Widget _buildOverlayButton(
BuildContext context, BuildContext context,
EntryAction action, EntryAction action,
BorderRadius borderRadius, BorderRadius? borderRadius,
) { ) {
Widget child; Widget child;
if (action == EntryAction.videoTogglePlay) { if (action == EntryAction.videoTogglePlay) {
@ -75,7 +67,7 @@ class VideoControlRow extends StatelessWidget {
onPressed: () => onActionSelected(action), onPressed: () => onActionSelected(action),
); );
} else { } else {
final enabled = action == EntryAction.openVideoPlayer ? !entry.trashed : true; final enabled = action == EntryAction.openVideoPlayer ? canOpenVideoPlayer : true;
child = IconButton( child = IconButton(
icon: action.getIcon(), icon: action.getIcon(),
onPressed: enabled ? () => onActionSelected(action) : null, onPressed: enabled ? () => onActionSelected(action) : null,
@ -83,13 +75,15 @@ class VideoControlRow extends StatelessWidget {
); );
} }
child = Padding( if (borderRadius != null) {
padding: EdgeInsets.only( child = Padding(
left: borderRadius.topLeft.x > 0 ? padding / 3 : 0, padding: EdgeInsets.only(
right: borderRadius.topRight.x > 0 ? padding / 3 : 0, left: borderRadius.topLeft.x > 0 ? padding / 3 : 0,
), right: borderRadius.topRight.x > 0 ? padding / 3 : 0,
child: child, ),
); child: child,
);
}
return OverlayButton( return OverlayButton(
scale: scale, scale: scale,

View file

@ -78,9 +78,9 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
), ),
), ),
VideoControlRow( VideoControlRow(
entry: entry,
controller: controller, controller: controller,
scale: scale, scale: scale,
canOpenVideoPlayer: !entry.trashed,
onActionSelected: widget.onActionSelected, onActionSelected: widget.onActionSelected,
), ),
], ],