settings: video controls preview
This commit is contained in:
parent
da959874fc
commit
71efe696c1
4 changed files with 76 additions and 69 deletions
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in a new issue