#316 wallpaper: scroll effect option

This commit is contained in:
Thibault Deckers 2022-09-29 10:41:25 +02:00
parent 38b9f84af0
commit 3d0e079df2
8 changed files with 160 additions and 44 deletions

View file

@ -14,6 +14,7 @@ All notable changes to this project will be documented in this file.
- Stats: open full top listings
- Slideshow: option for no transition
- Widget: tap action setting
- Wallpaper: scroll effect option
### Fixed

View file

@ -861,6 +861,8 @@
"viewerInfoSearchSuggestionResolution": "Resolution",
"viewerInfoSearchSuggestionRights": "Rights",
"wallpaperUseScrollEffect": "Use scroll effect on home screen",
"tagEditorPageTitle": "Edit Tags",
"tagEditorPageNewTagFieldLabel": "New tag",
"tagEditorPageAddTagTooltip": "Add tag",

View file

@ -676,6 +676,8 @@
"viewerInfoSearchSuggestionResolution": "Résolution",
"viewerInfoSearchSuggestionRights": "Droits",
"wallpaperUseScrollEffect": "Utiliser leffet de défilement sur lécran daccueil",
"tagEditorPageTitle": "Modifier les libellés",
"tagEditorPageNewTagFieldLabel": "Nouveau libellé",
"tagEditorPageAddTagTooltip": "Ajouter le libellé",

View file

@ -676,6 +676,8 @@
"viewerInfoSearchSuggestionResolution": "해상도",
"viewerInfoSearchSuggestionRights": "권리",
"wallpaperUseScrollEffect": "홈 화면에 스크롤 효과 사용",
"tagEditorPageTitle": "태그 수정",
"tagEditorPageNewTagFieldLabel": "새 태그",
"tagEditorPageAddTagTooltip": "태그 추가",

View file

@ -67,7 +67,21 @@ class _AvesSelectionDialogState<T> extends State<AvesSelectionDialog<T>> {
padding: const EdgeInsets.all(16),
child: Text(message),
),
...widget.options.entries.map((kv) => _buildRadioListTile(kv.key, kv.value, needConfirmation)),
...widget.options.entries.map((kv) {
final value = kv.key;
final title = kv.value;
return SelectionRadioListTile(
// key is expected by test driver
key: Key(value.toString()),
value: value,
title: title,
optionSubtitleBuilder: widget.optionSubtitleBuilder,
needConfirmation: needConfirmation,
dense: widget.dense,
getGroupValue: () => _selectedValue,
setGroupValue: (v) => setState(() => _selectedValue = v),
);
}),
],
actions: [
TextButton(
@ -82,17 +96,39 @@ class _AvesSelectionDialogState<T> extends State<AvesSelectionDialog<T>> {
],
);
}
}
Widget _buildRadioListTile(T value, String title, bool needConfirmation) {
final subtitle = widget.optionSubtitleBuilder?.call(value);
class SelectionRadioListTile<T> extends StatelessWidget {
final T value;
final String title;
final TextBuilder<T>? optionSubtitleBuilder;
final bool needConfirmation;
final bool? dense;
final T Function() getGroupValue;
final void Function(T value) setGroupValue;
const SelectionRadioListTile({
super.key,
required this.value,
required this.title,
this.optionSubtitleBuilder,
required this.needConfirmation,
this.dense,
required this.getGroupValue,
required this.setGroupValue,
});
@override
Widget build(BuildContext context) {
final subtitle = optionSubtitleBuilder?.call(value);
return ReselectableRadioListTile<T>(
// key is expected by test driver
key: Key(value.toString()),
value: value,
groupValue: _selectedValue,
groupValue: getGroupValue(),
onChanged: (v) {
if (needConfirmation) {
setState(() => _selectedValue = v as T);
setGroupValue(v as T);
} else {
Navigator.pop(context, v);
}
@ -112,7 +148,7 @@ class _AvesSelectionDialogState<T> extends State<AvesSelectionDialog<T>> {
maxLines: 1,
)
: null,
dense: widget.dense,
dense: dense,
);
}
}

View file

@ -0,0 +1,53 @@
import 'package:aves/model/device.dart';
import 'package:aves/model/wallpaper_target.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
import 'package:flutter/material.dart';
import 'package:tuple/tuple.dart';
import 'aves_dialog.dart';
class WallpaperSettingsDialog extends StatefulWidget {
const WallpaperSettingsDialog({super.key});
@override
State<WallpaperSettingsDialog> createState() => _WallpaperSettingsDialogState();
}
class _WallpaperSettingsDialogState extends State<WallpaperSettingsDialog> {
WallpaperTarget _selectedTarget = WallpaperTarget.home;
bool _useScrollEffect = true;
@override
Widget build(BuildContext context) {
return AvesDialog(
scrollableContent: [
if (device.canSetLockScreenWallpaper)
...WallpaperTarget.values.map((value) {
return SelectionRadioListTile(
value: value,
title: value.getName(context),
needConfirmation: true,
getGroupValue: () => _selectedTarget,
setGroupValue: (v) => setState(() => _selectedTarget = v),
);
}),
SwitchListTile(
value: _useScrollEffect,
onChanged: (v) => setState(() => _useScrollEffect = v),
title: Text(context.l10n.wallpaperUseScrollEffect),
)
],
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
),
TextButton(
onPressed: () => Navigator.pop(context, Tuple2<WallpaperTarget, bool>(_selectedTarget, _useScrollEffect)),
child: Text(context.l10n.applyButtonLabel),
),
],
);
}
}

View file

@ -2,14 +2,13 @@ import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:aves/model/device.dart';
import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_images.dart';
import 'package:aves/model/wallpaper_target.dart';
import 'package:aves/services/wallpaper_service.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
import 'package:aves/widgets/dialogs/wallpaper_settings_dialog.dart';
import 'package:aves/widgets/viewer/overlay/common.dart';
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
import 'package:aves/widgets/viewer/video/conductor.dart';
@ -18,6 +17,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:latlong2/latlong.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
class WallpaperButtons extends StatelessWidget with FeedbackMixin {
final AvesEntry entry;
@ -56,19 +56,14 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
Future<void> _setWallpaper(BuildContext context) async {
final l10n = context.l10n;
var target = WallpaperTarget.home;
if (device.canSetLockScreenWallpaper) {
final value = await showDialog<WallpaperTarget>(
context: context,
builder: (context) => AvesSelectionDialog<WallpaperTarget>(
initialValue: WallpaperTarget.home,
options: Map.fromEntries(WallpaperTarget.values.map((v) => MapEntry(v, v.getName(context)))),
confirmationButtonLabel: l10n.continueButtonLabel,
),
);
if (value == null) return;
target = value;
}
final value = await showDialog<Tuple2<WallpaperTarget, bool>>(
context: context,
builder: (context) => const WallpaperSettingsDialog(),
);
if (value == null) return;
final target = value.item1;
final useScrollEffect = value.item2;
final reportController = StreamController.broadcast();
unawaited(showOpReport(
@ -76,17 +71,15 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
opStream: reportController.stream,
));
final viewState = context.read<ViewStateConductor>().getOrCreateController(entry).value;
final viewportSize = viewState.viewportSize;
final contentSize = viewState.contentSize;
final scale = viewState.scale;
if (viewportSize == null || contentSize == null || contentSize.isEmpty || scale == null) return;
var region = _getVisibleRegion(context);
if (region == null) return;
final center = (contentSize / 2 - viewState.position / scale) as Size;
final regionSize = viewportSize / scale;
final regionTopLeft = (center - regionSize / 2) as Offset;
final region = Rect.fromLTWH(regionTopLeft.dx, regionTopLeft.dy, regionSize.width, regionSize.height);
final bytes = await _getBytes(context, scale, region);
if (useScrollEffect) {
final deltaX = min(region.left, entry.displaySize.width - region.right);
region = Rect.fromLTRB(region.left - deltaX, region.top, region.right + deltaX, region.bottom);
}
final bytes = await _getBytes(context, region);
final success = bytes != null && await WallpaperService.set(bytes, target);
unawaited(reportController.close());
@ -98,9 +91,25 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
}
}
Future<Uint8List?> _getBytes(BuildContext context, double scale, Rect displayRegion) async {
Rect? _getVisibleRegion(BuildContext context) {
final viewState = context.read<ViewStateConductor>().getOrCreateController(entry).value;
final viewportSize = viewState.viewportSize;
final contentSize = viewState.contentSize;
final scale = viewState.scale;
if (viewportSize == null || contentSize == null || contentSize.isEmpty || scale == null) return null;
final center = (contentSize / 2 - viewState.position / scale) as Size;
final regionSize = viewportSize / scale;
final regionTopLeft = (center - regionSize / 2) as Offset;
return Rect.fromLTWH(regionTopLeft.dx, regionTopLeft.dy, regionSize.width, regionSize.height);
}
Future<Uint8List?> _getBytes(BuildContext context, Rect displayRegion) async {
final viewState = context.read<ViewStateConductor>().getOrCreateController(entry).value;
final scale = viewState.scale;
final displaySize = entry.displaySize;
if (displaySize.isEmpty) return null;
if (displaySize.isEmpty || scale == null) return null;
var storageRegion = Rectangle(
displayRegion.left,

View file

@ -9,7 +9,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"el": [
@ -22,7 +23,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"es": [
@ -51,7 +53,8 @@
"settingsConfirmationAfterMoveToBinItems",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle",
"viewerInfoLabelDescription"
"viewerInfoLabelDescription",
"wallpaperUseScrollEffect"
],
"id": [
@ -64,7 +67,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"it": [
@ -77,7 +81,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"ja": [
@ -107,7 +112,8 @@
"settingsViewerGestureSideTapNext",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle",
"viewerInfoLabelDescription"
"viewerInfoLabelDescription",
"wallpaperUseScrollEffect"
],
"nl": [
@ -120,7 +126,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"pt": [
@ -133,7 +140,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"ru": [
@ -146,7 +154,8 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
],
"tr": [
@ -204,7 +213,8 @@
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle",
"viewerSetWallpaperButtonLabel",
"viewerInfoLabelDescription"
"viewerInfoLabelDescription",
"wallpaperUseScrollEffect"
],
"zh": [
@ -217,6 +227,7 @@
"albumGroupType",
"albumMimeTypeMixed",
"settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle"
"statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect"
]
}