tuple record migration
This commit is contained in:
parent
1117da068b
commit
a1a7b8e9e1
39 changed files with 163 additions and 241 deletions
|
@ -370,11 +370,6 @@ class Dependencies {
|
||||||
license: mit,
|
license: mit,
|
||||||
sourceUrl: 'https://github.com/brianegan/transparent_image',
|
sourceUrl: 'https://github.com/brianegan/transparent_image',
|
||||||
),
|
),
|
||||||
Dependency(
|
|
||||||
name: 'Tuple',
|
|
||||||
license: bsd2,
|
|
||||||
sourceUrl: 'https://github.com/google/tuple.dart',
|
|
||||||
),
|
|
||||||
Dependency(
|
Dependency(
|
||||||
name: 'Vector Math',
|
name: 'Vector Math',
|
||||||
license: '$zlib, $bsd3',
|
license: '$zlib, $bsd3',
|
||||||
|
|
|
@ -49,7 +49,6 @@ import 'package:material_color_utilities/material_color_utilities.dart';
|
||||||
import 'package:overlay_support/overlay_support.dart';
|
import 'package:overlay_support/overlay_support.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:screen_brightness/screen_brightness.dart';
|
import 'package:screen_brightness/screen_brightness.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart' as ul;
|
import 'package:url_launcher/url_launcher.dart' as ul;
|
||||||
|
|
||||||
class AvesApp extends StatefulWidget {
|
class AvesApp extends StatefulWidget {
|
||||||
|
@ -236,16 +235,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
: AvesScaffold(
|
: AvesScaffold(
|
||||||
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
|
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
|
||||||
);
|
);
|
||||||
return Selector<Settings, Tuple3<Locale?, AvesThemeBrightness, bool>>(
|
return Selector<Settings, (Locale?, AvesThemeBrightness, bool)>(
|
||||||
selector: (context, s) => Tuple3(
|
selector: (context, s) => (
|
||||||
s.locale,
|
s.locale,
|
||||||
s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
|
s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
|
||||||
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
|
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
|
||||||
),
|
),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
final settingsLocale = s.item1;
|
final (settingsLocale, themeBrightness, enableDynamicColor) = s;
|
||||||
final themeBrightness = s.item2;
|
|
||||||
final enableDynamicColor = s.item3;
|
|
||||||
|
|
||||||
AStyles.updateStylesForLocale(settings.appliedLocale);
|
AStyles.updateStylesForLocale(settings.appliedLocale);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class CollectionAppBar extends StatefulWidget {
|
class CollectionAppBar extends StatefulWidget {
|
||||||
final ValueNotifier<double> appBarHeightNotifier;
|
final ValueNotifier<double> appBarHeightNotifier;
|
||||||
|
@ -647,14 +646,14 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _configureView() async {
|
Future<void> _configureView() async {
|
||||||
final initialValue = Tuple4(
|
final initialValue = (
|
||||||
settings.collectionSortFactor,
|
settings.collectionSortFactor,
|
||||||
settings.collectionSectionFactor,
|
settings.collectionSectionFactor,
|
||||||
settings.getTileLayout(CollectionPage.routeName),
|
settings.getTileLayout(CollectionPage.routeName),
|
||||||
settings.collectionSortReverse,
|
settings.collectionSortReverse,
|
||||||
);
|
);
|
||||||
final extentController = context.read<TileExtentController>();
|
final extentController = context.read<TileExtentController>();
|
||||||
final value = await showDialog<Tuple4<EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool>>(
|
final value = await showDialog<(EntrySortFactor?, EntryGroupFactor?, TileLayout?, bool)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return TileViewDialog<EntrySortFactor, EntryGroupFactor, TileLayout>(
|
return TileViewDialog<EntrySortFactor, EntryGroupFactor, TileLayout>(
|
||||||
|
@ -672,10 +671,10 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
// wait for the dialog to hide as applying the change may block the UI
|
// wait for the dialog to hide as applying the change may block the UI
|
||||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||||
if (value != null && initialValue != value) {
|
if (value != null && initialValue != value) {
|
||||||
settings.collectionSortFactor = value.item1!;
|
settings.collectionSortFactor = value.$1!;
|
||||||
settings.collectionSectionFactor = value.item2!;
|
settings.collectionSectionFactor = value.$2!;
|
||||||
settings.setTileLayout(CollectionPage.routeName, value.item3!);
|
settings.setTileLayout(CollectionPage.routeName, value.$3!);
|
||||||
settings.collectionSortReverse = value.item4;
|
settings.collectionSortReverse = value.$4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,6 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class CollectionGrid extends StatefulWidget {
|
class CollectionGrid extends StatefulWidget {
|
||||||
final String settingsRouteKey;
|
final String settingsRouteKey;
|
||||||
|
@ -132,13 +131,10 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
|
||||||
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
||||||
builder: (context, thumbnailExtent, child) {
|
builder: (context, thumbnailExtent, child) {
|
||||||
assert(thumbnailExtent > 0);
|
assert(thumbnailExtent > 0);
|
||||||
return Selector<TileExtentController, Tuple4<double, int, double, double>>(
|
return Selector<TileExtentController, (double, int, double, double)>(
|
||||||
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
|
selector: (context, c) => (c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
|
||||||
builder: (context, c, child) {
|
builder: (context, c, child) {
|
||||||
final scrollableWidth = c.item1;
|
final (scrollableWidth, columnCount, tileSpacing, horizontalPadding) = c;
|
||||||
final columnCount = c.item2;
|
|
||||||
final tileSpacing = c.item3;
|
|
||||||
final horizontalPadding = c.item4;
|
|
||||||
final source = collection.source;
|
final source = collection.source;
|
||||||
return GridTheme(
|
return GridTheme(
|
||||||
extent: thumbnailExtent,
|
extent: thumbnailExtent,
|
||||||
|
@ -369,9 +365,7 @@ class _CollectionScaler extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final metrics = context.select<TileExtentController, Tuple2<double, double>>((v) => Tuple2(v.spacing, v.horizontalPadding));
|
final (tileSpacing, horizontalPadding) = context.select<TileExtentController, (double, double)>((v) => (v.spacing, v.horizontalPadding));
|
||||||
final tileSpacing = metrics.item1;
|
|
||||||
final horizontalPadding = metrics.item2;
|
|
||||||
final brightness = Theme.of(context).brightness;
|
final brightness = Theme.of(context).brightness;
|
||||||
final borderColor = DecoratedThumbnail.borderColor;
|
final borderColor = DecoratedThumbnail.borderColor;
|
||||||
final borderWidth = DecoratedThumbnail.borderWidth(context);
|
final borderWidth = DecoratedThumbnail.borderWidth(context);
|
||||||
|
|
|
@ -45,7 +45,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, EntryEditorMixin, EntryStorageMixin {
|
class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, EntryEditorMixin, EntryStorageMixin {
|
||||||
bool isVisible(
|
bool isVisible(
|
||||||
|
@ -709,7 +708,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
final sortedFilters = List<CollectionFilter>.from(filters)..sort();
|
final sortedFilters = List<CollectionFilter>.from(filters)..sort();
|
||||||
defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' ');
|
defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' ');
|
||||||
}
|
}
|
||||||
final result = await showDialog<Tuple2<AvesEntry?, String>>(
|
final result = await showDialog<(AvesEntry?, String)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AddShortcutDialog(
|
builder: (context) => AddShortcutDialog(
|
||||||
defaultName: defaultName ?? '',
|
defaultName: defaultName ?? '',
|
||||||
|
@ -719,8 +718,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|
||||||
final coverEntry = result.item1;
|
final (coverEntry, name) = result;
|
||||||
final name = result.item2;
|
|
||||||
if (name.isEmpty) return;
|
if (name.isEmpty) return;
|
||||||
|
|
||||||
await appService.pinToHomeScreen(name, coverEntry, filters: filters);
|
await appService.pinToHomeScreen(name, coverEntry, filters: filters);
|
||||||
|
|
|
@ -3,7 +3,6 @@ import 'dart:math';
|
||||||
import 'package:aves/utils/diff_match.dart';
|
import 'package:aves/utils/diff_match.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class AnimatedDiffText extends StatefulWidget {
|
class AnimatedDiffText extends StatefulWidget {
|
||||||
final String text;
|
final String text;
|
||||||
|
@ -71,10 +70,7 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: _diffs.map((diff) {
|
children: _diffs.map((diff) {
|
||||||
final oldText = diff.item1;
|
final (oldText, newText, oldSize, newSize) = diff;
|
||||||
final newText = diff.item2;
|
|
||||||
final oldSize = diff.item3;
|
|
||||||
final newSize = diff.item4;
|
|
||||||
final text = (_animation.value == 0 ? oldText : newText) ?? '';
|
final text = (_animation.value == 0 ? oldText : newText) ?? '';
|
||||||
return WidgetSpan(
|
return WidgetSpan(
|
||||||
child: AnimatedSize(
|
child: AnimatedSize(
|
||||||
|
@ -144,26 +140,22 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
|
||||||
..addAll(d.map((diff) {
|
..addAll(d.map((diff) {
|
||||||
final text = diff.text;
|
final text = diff.text;
|
||||||
final size = textSize(text);
|
final size = textSize(text);
|
||||||
switch (diff.operation) {
|
return switch (diff.operation) {
|
||||||
case Operation.delete:
|
Operation.delete => (text, null, size, Size.zero),
|
||||||
return Tuple4(text, null, size, Size.zero);
|
Operation.insert => (null, text, Size.zero, size),
|
||||||
case Operation.insert:
|
Operation.equal || _ => (text, text, size, size),
|
||||||
return Tuple4(null, text, Size.zero, size);
|
};
|
||||||
case Operation.equal:
|
|
||||||
default:
|
|
||||||
return Tuple4(text, text, size, size);
|
|
||||||
}
|
|
||||||
}).fold<List<_TextDiff>>([], (prev, v) {
|
}).fold<List<_TextDiff>>([], (prev, v) {
|
||||||
if (prev.isNotEmpty) {
|
if (prev.isNotEmpty) {
|
||||||
final last = prev.last;
|
final last = prev.last;
|
||||||
final prevNewText = last.item2;
|
final prevNewText = last.$2;
|
||||||
if (prevNewText == null) {
|
if (prevNewText == null) {
|
||||||
// previous diff is a deletion
|
// previous diff is a deletion
|
||||||
final thisOldText = v.item1;
|
final thisOldText = v.$1;
|
||||||
if (thisOldText == null) {
|
if (thisOldText == null) {
|
||||||
// this diff is an insertion
|
// this diff is an insertion
|
||||||
// merge deletion and insertion as a change operation
|
// merge deletion and insertion as a change operation
|
||||||
final change = Tuple4(last.item1, v.item2, last.item3, v.item4);
|
final change = (last.$1, v.$2, last.$3, v.$4);
|
||||||
return [...prev.take(prev.length - 1), change];
|
return [...prev.take(prev.length - 1), change];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,4 +165,4 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef _TextDiff = Tuple4<String?, String?, Size, Size>;
|
typedef _TextDiff = (String?, String?, Size, Size);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:aves/widgets/common/grid/sections/list_layout.dart';
|
||||||
import 'package:aves/widgets/common/grid/sections/section_layout.dart';
|
import 'package:aves/widgets/common/grid/sections/section_layout.dart';
|
||||||
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
|
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
|
@ -88,7 +87,7 @@ class FixedExtentSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
||||||
section: section,
|
section: section,
|
||||||
sectionGridIndex: listIndex * columnCount,
|
sectionGridIndex: listIndex * columnCount,
|
||||||
sectionChildIndex: sectionChildIndex,
|
sectionChildIndex: sectionChildIndex,
|
||||||
itemIndexRange: () => Tuple2(
|
itemIndexRange: () => (
|
||||||
(sectionChildIndex - 1) * columnCount,
|
(sectionChildIndex - 1) * columnCount,
|
||||||
sectionChildIndex * columnCount,
|
sectionChildIndex * columnCount,
|
||||||
),
|
),
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
|
@ -110,7 +109,7 @@ class MosaicSectionLayoutBuilder<T> extends SectionLayoutBuilder<T> {
|
||||||
section: section,
|
section: section,
|
||||||
sectionGridIndex: sectionGridIndex,
|
sectionGridIndex: sectionGridIndex,
|
||||||
sectionChildIndex: sectionChildIndex,
|
sectionChildIndex: sectionChildIndex,
|
||||||
itemIndexRange: () => isHeader ? const Tuple2(0, 0) : Tuple2(row.firstIndex, row.lastIndex + 1),
|
itemIndexRange: () => isHeader ? const (0, 0) : (row.firstIndex, row.lastIndex + 1),
|
||||||
sectionKey: sectionKey,
|
sectionKey: sectionKey,
|
||||||
headerExtent: headerExtent,
|
headerExtent: headerExtent,
|
||||||
itemSizes: row.itemWidths.map((v) => Size(v, row.height)).toList(),
|
itemSizes: row.itemWidths.map((v) => Size(v, row.height)).toList(),
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
typedef TileBuilder<T> = Widget Function(T item, Size tileSize);
|
typedef TileBuilder<T> = Widget Function(T item, Size tileSize);
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ abstract class SectionLayoutBuilder<T> {
|
||||||
required List<T> section,
|
required List<T> section,
|
||||||
required int sectionGridIndex,
|
required int sectionGridIndex,
|
||||||
required int sectionChildIndex,
|
required int sectionChildIndex,
|
||||||
required Tuple2<int, int> Function() itemIndexRange,
|
required (int, int) Function() itemIndexRange,
|
||||||
required SectionKey sectionKey,
|
required SectionKey sectionKey,
|
||||||
required double headerExtent,
|
required double headerExtent,
|
||||||
required List<Size> itemSizes,
|
required List<Size> itemSizes,
|
||||||
|
@ -68,8 +67,8 @@ abstract class SectionLayoutBuilder<T> {
|
||||||
|
|
||||||
final sectionItemCount = section.length;
|
final sectionItemCount = section.length;
|
||||||
final itemMinMax = itemIndexRange();
|
final itemMinMax = itemIndexRange();
|
||||||
final minItemIndex = itemMinMax.item1.clamp(0, sectionItemCount);
|
final minItemIndex = itemMinMax.$1.clamp(0, sectionItemCount);
|
||||||
final maxItemIndex = itemMinMax.item2.clamp(0, sectionItemCount);
|
final maxItemIndex = itemMinMax.$2.clamp(0, sectionItemCount);
|
||||||
final childrenCount = maxItemIndex - minItemIndex;
|
final childrenCount = maxItemIndex - minItemIndex;
|
||||||
final children = <Widget>[];
|
final children = <Widget>[];
|
||||||
for (var i = 0; i < childrenCount; i++) {
|
for (var i = 0; i < childrenCount; i++) {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import 'package:aves/widgets/dialogs/item_picker.dart';
|
||||||
import 'package:aves/widgets/dialogs/pick_dialogs/item_pick_page.dart';
|
import 'package:aves/widgets/dialogs/pick_dialogs/item_pick_page.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
import 'aves_dialog.dart';
|
import 'aves_dialog.dart';
|
||||||
|
|
||||||
|
@ -142,7 +141,7 @@ class _AddShortcutDialogState extends State<AddShortcutDialog> {
|
||||||
|
|
||||||
void _submit(BuildContext context) {
|
void _submit(BuildContext context) {
|
||||||
if (_isValidNotifier.value) {
|
if (_isValidNotifier.value) {
|
||||||
Navigator.maybeOf(context)?.pop(Tuple2<AvesEntry?, String>(_coverEntry, _nameController.text));
|
Navigator.maybeOf(context)?.pop<(AvesEntry?, String)>((_coverEntry, _nameController.text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class CoverSelectionDialog extends StatefulWidget {
|
class CoverSelectionDialog extends StatefulWidget {
|
||||||
static const routeName = '/dialog/select_cover';
|
static const routeName = '/dialog/select_cover';
|
||||||
|
@ -81,8 +80,8 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final tabs = <Tuple2<Tab, Widget>>[
|
final tabs = <(Tab, Widget)>[
|
||||||
Tuple2(
|
(
|
||||||
_buildTab(
|
_buildTab(
|
||||||
context,
|
context,
|
||||||
const Key('tab-entry'),
|
const Key('tab-entry'),
|
||||||
|
@ -92,7 +91,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
Column(children: _buildEntryOptions()),
|
Column(children: _buildEntryOptions()),
|
||||||
),
|
),
|
||||||
if (showAppTab)
|
if (showAppTab)
|
||||||
Tuple2(
|
(
|
||||||
_buildTab(
|
_buildTab(
|
||||||
context,
|
context,
|
||||||
const Key('tab-package'),
|
const Key('tab-package'),
|
||||||
|
@ -102,7 +101,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
Column(children: _buildAppOptions()),
|
Column(children: _buildAppOptions()),
|
||||||
),
|
),
|
||||||
if (showColorTab)
|
if (showColorTab)
|
||||||
Tuple2(
|
(
|
||||||
_buildTab(
|
_buildTab(
|
||||||
context,
|
context,
|
||||||
const Key('tab-color'),
|
const Key('tab-color'),
|
||||||
|
@ -131,7 +130,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
indicatorWeight: tabIndicatorWeight,
|
indicatorWeight: tabIndicatorWeight,
|
||||||
tabs: tabs.map((t) => t.item1).toList(),
|
tabs: tabs.map((t) => t.$1).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
|
@ -140,7 +139,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
children: tabs
|
children: tabs
|
||||||
.map((t) => SingleChildScrollView(
|
.map((t) => SingleChildScrollView(
|
||||||
child: t.item2,
|
child: t.$2,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
@ -165,7 +164,7 @@ class _CoverSelectionDialogState extends State<CoverSelectionDialog> {
|
||||||
final entry = _isCustomEntry ? _customEntry : null;
|
final entry = _isCustomEntry ? _customEntry : null;
|
||||||
final package = _isCustomPackage ? _customPackage : null;
|
final package = _isCustomPackage ? _customPackage : null;
|
||||||
final color = _isCustomColor ? _customColor : null;
|
final color = _isCustomColor ? _customColor : null;
|
||||||
return Navigator.maybeOf(context)?.pop(Tuple3<AvesEntry?, String?, Color?>(entry, package, color));
|
return Navigator.maybeOf(context)?.pop<(AvesEntry?, String?, Color?)>((entry, package, color));
|
||||||
},
|
},
|
||||||
child: Text(l10n.applyButtonLabel),
|
child: Text(l10n.applyButtonLabel),
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,7 +27,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
Future<String?> pickAlbum({
|
Future<String?> pickAlbum({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
|
@ -94,8 +93,8 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListenableProvider<ValueNotifier<AppMode>>.value(
|
return ListenableProvider<ValueNotifier<AppMode>>.value(
|
||||||
value: ValueNotifier(AppMode.pickFilterInternal),
|
value: ValueNotifier(AppMode.pickFilterInternal),
|
||||||
child: Selector<Settings, Tuple2<AlbumChipGroupFactor, ChipSortFactor>>(
|
child: Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor)>(
|
||||||
selector: (context, s) => Tuple2(s.albumGroupFactor, s.albumSortFactor),
|
selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
stream: source.eventBus.on<AlbumsChangedEvent>(),
|
||||||
|
|
|
@ -11,14 +11,13 @@ import 'package:aves/widgets/common/identity/highlight_title.dart';
|
||||||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
import 'aves_dialog.dart';
|
import 'aves_dialog.dart';
|
||||||
|
|
||||||
class TileViewDialog<S, G, L> extends StatefulWidget {
|
class TileViewDialog<S, G, L> extends StatefulWidget {
|
||||||
static const routeName = '/dialog/tile_view';
|
static const routeName = '/dialog/tile_view';
|
||||||
|
|
||||||
final Tuple4<S?, G?, L?, bool> initialValue;
|
final (S? sort, G? group, L? layout, bool reverse) initialValue;
|
||||||
final List<TileViewDialogOption<S>> sortOptions;
|
final List<TileViewDialogOption<S>> sortOptions;
|
||||||
final List<TileViewDialogOption<G>> groupOptions;
|
final List<TileViewDialogOption<G>> groupOptions;
|
||||||
final List<TileViewDialogOption<L>> layoutOptions;
|
final List<TileViewDialogOption<L>> layoutOptions;
|
||||||
|
@ -63,10 +62,10 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final initialValue = widget.initialValue;
|
final initialValue = widget.initialValue;
|
||||||
_selectedSort = initialValue.item1;
|
_selectedSort = initialValue.$1;
|
||||||
_selectedGroup = initialValue.item2;
|
_selectedGroup = initialValue.$2;
|
||||||
_selectedLayout = initialValue.item3;
|
_selectedLayout = initialValue.$3;
|
||||||
_reverseSort = initialValue.item4;
|
_reverseSort = initialValue.$4;
|
||||||
|
|
||||||
final extentController = tileExtentController;
|
final extentController = tileExtentController;
|
||||||
final columnRange = extentController.effectiveColumnRange;
|
final columnRange = extentController.effectiveColumnRange;
|
||||||
|
@ -147,7 +146,7 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
|
||||||
key: const Key('button-apply'),
|
key: const Key('button-apply'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
tileExtentController.setUserPreferredColumnCount(_columnCountNotifier.value);
|
tileExtentController.setUserPreferredColumnCount(_columnCountNotifier.value);
|
||||||
Navigator.maybeOf(context)?.pop(Tuple4(_selectedSort, _selectedGroup, _selectedLayout, _reverseSort));
|
Navigator.maybeOf(context)?.pop<(S?, G?, L?, bool)>((_selectedSort, _selectedGroup, _selectedLayout, _reverseSort));
|
||||||
},
|
},
|
||||||
child: Text(l10n.applyButtonLabel),
|
child: Text(l10n.applyButtonLabel),
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.dart';
|
import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.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:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class WallpaperSettingsDialog extends StatefulWidget {
|
class WallpaperSettingsDialog extends StatefulWidget {
|
||||||
static const routeName = '/dialog/wallpaper_settings';
|
static const routeName = '/dialog/wallpaper_settings';
|
||||||
|
@ -43,7 +42,7 @@ class _WallpaperSettingsDialogState extends State<WallpaperSettingsDialog> {
|
||||||
actions: [
|
actions: [
|
||||||
const CancelButton(),
|
const CancelButton(),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.maybeOf(context)?.pop(Tuple2<WallpaperTarget, bool>(_selectedTarget, _useScrollEffect)),
|
onPressed: () => Navigator.maybeOf(context)?.pop<(WallpaperTarget, bool)>((_selectedTarget, _useScrollEffect)),
|
||||||
child: Text(context.l10n.applyButtonLabel),
|
child: Text(context.l10n.applyButtonLabel),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class TransformControlPanel extends StatefulWidget {
|
class TransformControlPanel extends StatefulWidget {
|
||||||
final AvesEntry entry;
|
final AvesEntry entry;
|
||||||
|
@ -32,7 +31,7 @@ class TransformControlPanel extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TransformControlPanelState extends State<TransformControlPanel> with TickerProviderStateMixin {
|
class _TransformControlPanelState extends State<TransformControlPanel> with TickerProviderStateMixin {
|
||||||
late final List<Tuple2<WidgetBuilder, WidgetBuilder>> _tabs;
|
late final List<(WidgetBuilder, WidgetBuilder)> _tabs;
|
||||||
late final TabController _tabController;
|
late final TabController _tabController;
|
||||||
|
|
||||||
static const padding = EditorControlPanel.padding;
|
static const padding = EditorControlPanel.padding;
|
||||||
|
@ -41,11 +40,11 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabs = [
|
_tabs = [
|
||||||
Tuple2(
|
(
|
||||||
(context) => Tab(text: context.l10n.editorTransformCrop),
|
(context) => Tab(text: context.l10n.editorTransformCrop),
|
||||||
(context) => const CropControlPanel(),
|
(context) => const CropControlPanel(),
|
||||||
),
|
),
|
||||||
Tuple2(
|
(
|
||||||
(context) => Tab(text: context.l10n.editorTransformRotate),
|
(context) => Tab(text: context.l10n.editorTransformRotate),
|
||||||
(context) => const RotationControlPanel(),
|
(context) => const RotationControlPanel(),
|
||||||
),
|
),
|
||||||
|
@ -74,7 +73,7 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return AnimatedSwitcher(
|
return AnimatedSwitcher(
|
||||||
duration: context.select<DurationsData, Duration>((v) => v.formTransition),
|
duration: context.select<DurationsData, Duration>((v) => v.formTransition),
|
||||||
child: _tabs[_tabController.index].item2(context),
|
child: _tabs[_tabController.index].$2(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -87,7 +86,7 @@ class _TransformControlPanelState extends State<TransformControlPanel> with Tick
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBar(
|
child: TabBar(
|
||||||
tabs: _tabs.map((v) => v.item1(context)).toList(),
|
tabs: _tabs.map((v) => v.$1(context)).toList(),
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: padding),
|
padding: const EdgeInsets.symmetric(horizontal: padding),
|
||||||
indicatorSize: TabBarIndicatorSize.label,
|
indicatorSize: TabBarIndicatorSize.label,
|
||||||
|
|
|
@ -17,7 +17,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class AlbumListPage extends StatelessWidget {
|
class AlbumListPage extends StatelessWidget {
|
||||||
static const routeName = '/albums';
|
static const routeName = '/albums';
|
||||||
|
@ -27,12 +26,12 @@ class AlbumListPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
return Selector<Settings, Tuple4<AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>>>(
|
return Selector<Settings, (AlbumChipGroupFactor, ChipSortFactor, bool, Set<CollectionFilter>)>(
|
||||||
selector: (context, s) => Tuple4(s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters),
|
selector: (context, s) => (s.albumGroupFactor, s.albumSortFactor, s.albumSortReverse, s.pinnedFilters),
|
||||||
shouldRebuild: (t1, t2) {
|
shouldRebuild: (t1, t2) {
|
||||||
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
|
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
|
||||||
const eq = DeepCollectionEquality();
|
const eq = DeepCollectionEquality();
|
||||||
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3) && eq.equals(t1.item4, t2.item4));
|
return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3) && eq.equals(t1.$4, t2.$4));
|
||||||
},
|
},
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return ValueListenableBuilder<bool>(
|
return ValueListenableBuilder<bool>(
|
||||||
|
|
|
@ -32,7 +32,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with EntryStorageMixin {
|
class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with EntryStorageMixin {
|
||||||
final Iterable<FilterGridItem<AlbumFilter>> _items;
|
final Iterable<FilterGridItem<AlbumFilter>> _items;
|
||||||
|
@ -168,14 +167,14 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> configureView(BuildContext context) async {
|
Future<void> configureView(BuildContext context) async {
|
||||||
final initialValue = Tuple4(
|
final initialValue = (
|
||||||
sortFactor,
|
sortFactor,
|
||||||
settings.albumGroupFactor,
|
settings.albumGroupFactor,
|
||||||
tileLayout,
|
tileLayout,
|
||||||
sortReverse,
|
sortReverse,
|
||||||
);
|
);
|
||||||
final extentController = context.read<TileExtentController>();
|
final extentController = context.read<TileExtentController>();
|
||||||
final value = await showDialog<Tuple4<ChipSortFactor?, AlbumChipGroupFactor?, TileLayout?, bool>>(
|
final value = await showDialog<(ChipSortFactor?, AlbumChipGroupFactor?, TileLayout?, bool)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return TileViewDialog<ChipSortFactor, AlbumChipGroupFactor, TileLayout>(
|
return TileViewDialog<ChipSortFactor, AlbumChipGroupFactor, TileLayout>(
|
||||||
|
@ -192,10 +191,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
// wait for the dialog to hide as applying the change may block the UI
|
// wait for the dialog to hide as applying the change may block the UI
|
||||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||||
if (value != null && initialValue != value) {
|
if (value != null && initialValue != value) {
|
||||||
sortFactor = value.item1!;
|
sortFactor = value.$1!;
|
||||||
settings.albumGroupFactor = value.item2!;
|
settings.albumGroupFactor = value.$2!;
|
||||||
tileLayout = value.item3!;
|
tileLayout = value.$3!;
|
||||||
sortReverse = value.item4;
|
sortReverse = value.$4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, VaultAwareMixin {
|
abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, VaultAwareMixin {
|
||||||
Iterable<FilterGridItem<T>> get allItems;
|
Iterable<FilterGridItem<T>> get allItems;
|
||||||
|
@ -216,14 +215,14 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> configureView(BuildContext context) async {
|
Future<void> configureView(BuildContext context) async {
|
||||||
final initialValue = Tuple4(
|
final initialValue = (
|
||||||
sortFactor,
|
sortFactor,
|
||||||
null,
|
null,
|
||||||
tileLayout,
|
tileLayout,
|
||||||
sortReverse,
|
sortReverse,
|
||||||
);
|
);
|
||||||
final extentController = context.read<TileExtentController>();
|
final extentController = context.read<TileExtentController>();
|
||||||
final value = await showDialog<Tuple4<ChipSortFactor?, void, TileLayout?, bool>>(
|
final value = await showDialog<(ChipSortFactor?, void, TileLayout?, bool)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return TileViewDialog<ChipSortFactor, void, TileLayout>(
|
return TileViewDialog<ChipSortFactor, void, TileLayout>(
|
||||||
|
@ -239,9 +238,9 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
// wait for the dialog to hide as applying the change may block the UI
|
// wait for the dialog to hide as applying the change may block the UI
|
||||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||||
if (value != null && initialValue != value) {
|
if (value != null && initialValue != value) {
|
||||||
sortFactor = value.item1!;
|
sortFactor = value.$1!;
|
||||||
tileLayout = value.item3!;
|
tileLayout = value.$3!;
|
||||||
sortReverse = value.item4;
|
sortReverse = value.$4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +327,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
final existingCover = covers.of(filter);
|
final existingCover = covers.of(filter);
|
||||||
final entryId = existingCover?.$1;
|
final entryId = existingCover?.$1;
|
||||||
final customEntry = entryId != null ? context.read<CollectionSource>().visibleEntries.firstWhereOrNull((entry) => entry.id == entryId) : null;
|
final customEntry = entryId != null ? context.read<CollectionSource>().visibleEntries.firstWhereOrNull((entry) => entry.id == entryId) : null;
|
||||||
final selectedCover = await showDialog<Tuple3<AvesEntry?, String?, Color?>>(
|
final selectedCover = await showDialog<(AvesEntry?, String?, Color?)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => CoverSelectionDialog(
|
builder: (context) => CoverSelectionDialog(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
|
@ -344,9 +343,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
||||||
context.read<AvesColorsData>().clearAppColor(filter.album);
|
context.read<AvesColorsData>().clearAppColor(filter.album);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedEntry = selectedCover.item1;
|
final (selectedEntry, selectedPackage, selectedColor) = selectedCover;
|
||||||
final selectedPackage = selectedCover.item2;
|
|
||||||
final selectedColor = selectedCover.item3;
|
|
||||||
await covers.set(
|
await covers.set(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
entryId: selectedEntry?.id,
|
entryId: selectedEntry?.id,
|
||||||
|
|
|
@ -46,7 +46,6 @@ import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
typedef QueryTest<T extends CollectionFilter> = List<FilterGridItem<T>> Function(BuildContext context, List<FilterGridItem<T>> filters, String query);
|
typedef QueryTest<T extends CollectionFilter> = List<FilterGridItem<T>> Function(BuildContext context, List<FilterGridItem<T>> filters, String query);
|
||||||
|
|
||||||
|
@ -317,13 +316,10 @@ class _FilterGridContentState<T extends CollectionFilter> extends State<_FilterG
|
||||||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||||
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
||||||
builder: (context, thumbnailExtent, child) {
|
builder: (context, thumbnailExtent, child) {
|
||||||
return Selector<TileExtentController, Tuple4<double, int, double, double>>(
|
return Selector<TileExtentController, (double, int, double, double)>(
|
||||||
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
|
selector: (context, c) => (c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
|
||||||
builder: (context, c, child) {
|
builder: (context, c, child) {
|
||||||
final scrollableWidth = c.item1;
|
final (scrollableWidth, columnCount, tileSpacing, horizontalPadding) = c;
|
||||||
final columnCount = c.item2;
|
|
||||||
final tileSpacing = c.item3;
|
|
||||||
final horizontalPadding = c.item4;
|
|
||||||
// do not listen for animation delay change
|
// do not listen for animation delay change
|
||||||
final target = context.read<DurationsData>().staggeredAnimationPageTarget;
|
final target = context.read<DurationsData>().staggeredAnimationPageTarget;
|
||||||
final tileAnimationDelay = context.read<TileExtentController>().getTileAnimationDelay(target);
|
final tileAnimationDelay = context.read<TileExtentController>().getTileAnimationDelay(target);
|
||||||
|
@ -571,9 +567,7 @@ class _FilterScaler<T extends CollectionFilter> extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
|
||||||
final metrics = context.select<TileExtentController, Tuple2<double, double>>((v) => Tuple2(v.spacing, v.horizontalPadding));
|
final (tileSpacing, horizontalPadding) = context.select<TileExtentController, (double, double)>((v) => (v.spacing, v.horizontalPadding));
|
||||||
final tileSpacing = metrics.item1;
|
|
||||||
final horizontalPadding = metrics.item2;
|
|
||||||
final brightness = Theme.of(context).brightness;
|
final brightness = Theme.of(context).brightness;
|
||||||
return GridScaleGestureDetector<FilterGridItem<T>>(
|
return GridScaleGestureDetector<FilterGridItem<T>>(
|
||||||
scrollableKey: scrollableKey,
|
scrollableKey: scrollableKey,
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class CountryListPage extends StatelessWidget {
|
class CountryListPage extends StatelessWidget {
|
||||||
static const routeName = '/countries';
|
static const routeName = '/countries';
|
||||||
|
@ -23,12 +22,12 @@ class CountryListPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
|
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
|
||||||
selector: (context, s) => Tuple3(s.countrySortFactor, s.countrySortReverse, s.pinnedFilters),
|
selector: (context, s) => (s.countrySortFactor, s.countrySortReverse, s.pinnedFilters),
|
||||||
shouldRebuild: (t1, t2) {
|
shouldRebuild: (t1, t2) {
|
||||||
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
|
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
|
||||||
const eq = DeepCollectionEquality();
|
const eq = DeepCollectionEquality();
|
||||||
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3));
|
return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
|
||||||
},
|
},
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class PlaceListPage extends StatelessWidget {
|
class PlaceListPage extends StatelessWidget {
|
||||||
static const routeName = '/places';
|
static const routeName = '/places';
|
||||||
|
@ -23,12 +22,12 @@ class PlaceListPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
|
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
|
||||||
selector: (context, s) => Tuple3(s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
|
selector: (context, s) => (s.placeSortFactor, s.placeSortReverse, s.pinnedFilters),
|
||||||
shouldRebuild: (t1, t2) {
|
shouldRebuild: (t1, t2) {
|
||||||
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
|
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
|
||||||
const eq = DeepCollectionEquality();
|
const eq = DeepCollectionEquality();
|
||||||
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3));
|
return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
|
||||||
},
|
},
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class StateListPage extends StatelessWidget {
|
class StateListPage extends StatelessWidget {
|
||||||
static const routeName = '/states';
|
static const routeName = '/states';
|
||||||
|
@ -29,12 +28,12 @@ class StateListPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
|
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
|
||||||
selector: (context, s) => Tuple3(s.stateSortFactor, s.stateSortReverse, s.pinnedFilters),
|
selector: (context, s) => (s.stateSortFactor, s.stateSortReverse, s.pinnedFilters),
|
||||||
shouldRebuild: (t1, t2) {
|
shouldRebuild: (t1, t2) {
|
||||||
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
|
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
|
||||||
const eq = DeepCollectionEquality();
|
const eq = DeepCollectionEquality();
|
||||||
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3));
|
return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
|
||||||
},
|
},
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class TagListPage extends StatelessWidget {
|
class TagListPage extends StatelessWidget {
|
||||||
static const routeName = '/tags';
|
static const routeName = '/tags';
|
||||||
|
@ -23,12 +22,12 @@ class TagListPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final source = context.read<CollectionSource>();
|
final source = context.read<CollectionSource>();
|
||||||
return Selector<Settings, Tuple3<ChipSortFactor, bool, Set<CollectionFilter>>>(
|
return Selector<Settings, (ChipSortFactor, bool, Set<CollectionFilter>)>(
|
||||||
selector: (context, s) => Tuple3(s.tagSortFactor, s.tagSortReverse, s.pinnedFilters),
|
selector: (context, s) => (s.tagSortFactor, s.tagSortReverse, s.pinnedFilters),
|
||||||
shouldRebuild: (t1, t2) {
|
shouldRebuild: (t1, t2) {
|
||||||
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within `TupleN`
|
// `Selector` by default uses `DeepCollectionEquality`, which does not go deep in collections within records
|
||||||
const eq = DeepCollectionEquality();
|
const eq = DeepCollectionEquality();
|
||||||
return !(eq.equals(t1.item1, t2.item1) && eq.equals(t1.item2, t2.item2) && eq.equals(t1.item3, t2.item3));
|
return !(eq.equals(t1.$1, t2.$1) && eq.equals(t1.$2, t2.$2) && eq.equals(t1.$3, t2.$3));
|
||||||
},
|
},
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
return StreamBuilder(
|
return StreamBuilder(
|
||||||
|
|
|
@ -15,7 +15,6 @@ import 'package:aves/widgets/settings/navigation/drawer_tab_albums.dart';
|
||||||
import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart';
|
import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class NavigationDrawerEditorPage extends StatefulWidget {
|
class NavigationDrawerEditorPage extends StatefulWidget {
|
||||||
static const routeName = '/settings/navigation_drawer';
|
static const routeName = '/settings/navigation_drawer';
|
||||||
|
@ -65,8 +64,8 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final tabs = <Tuple2<Tab, Widget>>[
|
final tabs = <(Tab, Widget)>[
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsNavigationDrawerTabTypes),
|
Tab(text: l10n.settingsNavigationDrawerTabTypes),
|
||||||
DrawerFixedListTab<CollectionFilter?>(
|
DrawerFixedListTab<CollectionFilter?>(
|
||||||
items: _typeItems,
|
items: _typeItems,
|
||||||
|
@ -75,13 +74,13 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
|
||||||
title: (item) => DrawerFilterTitle(filter: item),
|
title: (item) => DrawerFilterTitle(filter: item),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsNavigationDrawerTabAlbums),
|
Tab(text: l10n.settingsNavigationDrawerTabAlbums),
|
||||||
DrawerAlbumTab(
|
DrawerAlbumTab(
|
||||||
items: _albumItems,
|
items: _albumItems,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsNavigationDrawerTabPages),
|
Tab(text: l10n.settingsNavigationDrawerTabPages),
|
||||||
DrawerFixedListTab<String>(
|
DrawerFixedListTab<String>(
|
||||||
items: _pageItems,
|
items: _pageItems,
|
||||||
|
@ -99,7 +98,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
|
||||||
automaticallyImplyLeading: !settings.useTvLayout,
|
automaticallyImplyLeading: !settings.useTvLayout,
|
||||||
title: Text(l10n.settingsNavigationDrawerEditorPageTitle),
|
title: Text(l10n.settingsNavigationDrawerEditorPageTitle),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: tabs.map((t) => t.item1).toList(),
|
tabs: tabs.map((t) => t.$1).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: WillPopScope(
|
body: WillPopScope(
|
||||||
|
@ -111,7 +110,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
|
||||||
},
|
},
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: tabs.map((t) => t.item2).toList(),
|
children: tabs.map((t) => t.$2).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,7 +13,6 @@ import 'package:aves/widgets/settings/privacy/file_picker/file_picker_page.dart'
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class HiddenItemsPage extends StatelessWidget {
|
class HiddenItemsPage extends StatelessWidget {
|
||||||
static const routeName = '/settings/hidden_items';
|
static const routeName = '/settings/hidden_items';
|
||||||
|
@ -23,12 +22,12 @@ class HiddenItemsPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final tabs = <Tuple2<Tab, Widget>>[
|
final tabs = <(Tab, Widget)>[
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsHiddenItemsTabFilters),
|
Tab(text: l10n.settingsHiddenItemsTabFilters),
|
||||||
const _HiddenFilters(),
|
const _HiddenFilters(),
|
||||||
),
|
),
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsHiddenItemsTabPaths),
|
Tab(text: l10n.settingsHiddenItemsTabPaths),
|
||||||
const _HiddenPaths(),
|
const _HiddenPaths(),
|
||||||
),
|
),
|
||||||
|
@ -41,12 +40,12 @@ class HiddenItemsPage extends StatelessWidget {
|
||||||
automaticallyImplyLeading: !settings.useTvLayout,
|
automaticallyImplyLeading: !settings.useTvLayout,
|
||||||
title: Text(l10n.settingsHiddenItemsPageTitle),
|
title: Text(l10n.settingsHiddenItemsPageTitle),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: tabs.map((t) => t.item1).toList(),
|
tabs: tabs.map((t) => t.$1).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: tabs.map((t) => t.item2).toList(),
|
children: tabs.map((t) => t.$2).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/settings/common/quick_actions/editor_page.dart';
|
import 'package:aves/widgets/settings/common/quick_actions/editor_page.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:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class CollectionActionEditorPage extends StatelessWidget {
|
class CollectionActionEditorPage extends StatelessWidget {
|
||||||
static const routeName = '/settings/collection_actions';
|
static const routeName = '/settings/collection_actions';
|
||||||
|
@ -15,8 +14,8 @@ class CollectionActionEditorPage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final tabs = <Tuple2<Tab, Widget>>[
|
final tabs = <(Tab, Widget)>[
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsCollectionQuickActionTabBrowsing),
|
Tab(text: l10n.settingsCollectionQuickActionTabBrowsing),
|
||||||
QuickActionEditorBody<EntrySetAction>(
|
QuickActionEditorBody<EntrySetAction>(
|
||||||
bannerText: context.l10n.settingsCollectionBrowsingQuickActionEditorBanner,
|
bannerText: context.l10n.settingsCollectionBrowsingQuickActionEditorBanner,
|
||||||
|
@ -27,7 +26,7 @@ class CollectionActionEditorPage extends StatelessWidget {
|
||||||
save: (actions) => settings.collectionBrowsingQuickActions = actions,
|
save: (actions) => settings.collectionBrowsingQuickActions = actions,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Tuple2(
|
(
|
||||||
Tab(text: l10n.settingsCollectionQuickActionTabSelecting),
|
Tab(text: l10n.settingsCollectionQuickActionTabSelecting),
|
||||||
QuickActionEditorBody<EntrySetAction>(
|
QuickActionEditorBody<EntrySetAction>(
|
||||||
bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner,
|
bannerText: context.l10n.settingsCollectionSelectionQuickActionEditorBanner,
|
||||||
|
@ -49,12 +48,12 @@ class CollectionActionEditorPage extends StatelessWidget {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(context.l10n.settingsCollectionQuickActionEditorPageTitle),
|
title: Text(context.l10n.settingsCollectionQuickActionEditorPageTitle),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: tabs.map((t) => t.item1).toList(),
|
tabs: tabs.map((t) => t.$1).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: tabs.map((t) => t.item2).toList(),
|
children: tabs.map((t) => t.$2).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:aves/widgets/settings/common/tiles.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';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class ViewerOverlayPage extends StatelessWidget {
|
class ViewerOverlayPage extends StatelessWidget {
|
||||||
static const routeName = '/settings/viewer/overlay';
|
static const routeName = '/settings/viewer/overlay';
|
||||||
|
@ -36,11 +35,10 @@ class ViewerOverlayPage extends StatelessWidget {
|
||||||
title: context.l10n.settingsViewerShowInformation,
|
title: context.l10n.settingsViewerShowInformation,
|
||||||
subtitle: context.l10n.settingsViewerShowInformationSubtitle,
|
subtitle: context.l10n.settingsViewerShowInformationSubtitle,
|
||||||
),
|
),
|
||||||
Selector<Settings, Tuple2<bool, bool>>(
|
Selector<Settings, (bool, bool)>(
|
||||||
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayRatingTags),
|
selector: (context, s) => (s.showOverlayInfo, s.showOverlayRatingTags),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
final showInfo = s.item1;
|
final (showInfo, current) = s;
|
||||||
final current = s.item2;
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
value: current,
|
value: current,
|
||||||
onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null,
|
onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null,
|
||||||
|
@ -48,11 +46,10 @@ class ViewerOverlayPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Selector<Settings, Tuple2<bool, bool>>(
|
Selector<Settings, (bool, bool)>(
|
||||||
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayShootingDetails),
|
selector: (context, s) => (s.showOverlayInfo, s.showOverlayShootingDetails),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
final showInfo = s.item1;
|
final (showInfo, current) = s;
|
||||||
final current = s.item2;
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
value: current,
|
value: current,
|
||||||
onChanged: showInfo ? (v) => settings.showOverlayShootingDetails = v : null,
|
onChanged: showInfo ? (v) => settings.showOverlayShootingDetails = v : null,
|
||||||
|
@ -60,11 +57,10 @@ class ViewerOverlayPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Selector<Settings, Tuple2<bool, bool>>(
|
Selector<Settings, (bool, bool)>(
|
||||||
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayDescription),
|
selector: (context, s) => (s.showOverlayInfo, s.showOverlayDescription),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
final showInfo = s.item1;
|
final (showInfo, current) = s;
|
||||||
final current = s.item2;
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
value: current,
|
value: current,
|
||||||
onChanged: showInfo ? (v) => settings.showOverlayDescription = v : null,
|
onChanged: showInfo ? (v) => settings.showOverlayDescription = v : null,
|
||||||
|
|
|
@ -39,7 +39,6 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, SingleEntryEditorMixin, EntryStorageMixin, VaultAwareMixin {
|
class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMixin, SingleEntryEditorMixin, EntryStorageMixin, VaultAwareMixin {
|
||||||
final AvesEntry mainEntry, pageEntry;
|
final AvesEntry mainEntry, pageEntry;
|
||||||
|
@ -325,7 +324,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _addShortcut(BuildContext context, AvesEntry targetEntry) async {
|
Future<void> _addShortcut(BuildContext context, AvesEntry targetEntry) async {
|
||||||
final result = await showDialog<Tuple2<AvesEntry?, String>>(
|
final result = await showDialog<(AvesEntry?, String)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AddShortcutDialog(
|
builder: (context) => AddShortcutDialog(
|
||||||
defaultName: targetEntry.bestTitle ?? '',
|
defaultName: targetEntry.bestTitle ?? '',
|
||||||
|
@ -334,7 +333,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
|
|
||||||
final name = result.item2;
|
final name = result.$2;
|
||||||
if (name.isEmpty) return;
|
if (name.isEmpty) return;
|
||||||
|
|
||||||
await appService.pinToHomeScreen(name, targetEntry, uri: targetEntry.uri);
|
await appService.pinToHomeScreen(name, targetEntry, uri: targetEntry.uri);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class ViewerDebugPage extends StatelessWidget {
|
class ViewerDebugPage extends StatelessWidget {
|
||||||
static const routeName = '/viewer/debug';
|
static const routeName = '/viewer/debug';
|
||||||
|
@ -26,11 +25,11 @@ class ViewerDebugPage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tabs = <Tuple2<Tab, Widget>>[
|
final tabs = <(Tab, Widget)>[
|
||||||
Tuple2(const Tab(text: 'Entry'), _buildEntryTabView()),
|
(const Tab(text: 'Entry'), _buildEntryTabView()),
|
||||||
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) Tuple2(const Tab(text: 'DB'), DbTab(entry: entry)),
|
if (context.select<ValueNotifier<AppMode>, bool>((vn) => vn.value != AppMode.view)) (const Tab(text: 'DB'), DbTab(entry: entry)),
|
||||||
Tuple2(const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
|
(const Tab(icon: Icon(AIcons.android)), MetadataTab(entry: entry)),
|
||||||
Tuple2(const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
|
(const Tab(icon: Icon(AIcons.image)), _buildThumbnailsTabView()),
|
||||||
];
|
];
|
||||||
return Directionality(
|
return Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -40,12 +39,12 @@ class ViewerDebugPage extends StatelessWidget {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Debug'),
|
title: const Text('Debug'),
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
tabs: tabs.map((t) => t.item1).toList(),
|
tabs: tabs.map((t) => t.$1).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
children: tabs.map((t) => t.item2).toList(),
|
children: tabs.map((t) => t.$2).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,9 +9,8 @@ import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
typedef XmpExtractedCard = Tuple2<Map<String, XmpProp>, List<XmpCardData>?>;
|
typedef XmpExtractedCard = (Map<String, XmpProp>, List<XmpCardData>?);
|
||||||
|
|
||||||
class XmpCard extends StatefulWidget {
|
class XmpCard extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -30,7 +29,7 @@ class XmpCard extends StatefulWidget {
|
||||||
directStruct = structByIndex[null];
|
directStruct = structByIndex[null];
|
||||||
|
|
||||||
final length = structByIndex.keys.whereNotNull().fold(0, max);
|
final length = structByIndex.keys.whereNotNull().fold(0, max);
|
||||||
indexedStructs = length > 0 ? [for (var i = 0; i < length; i++) structByIndex[i + 1] ?? const Tuple2({}, null)] : null;
|
indexedStructs = length > 0 ? [for (var i = 0; i < length; i++) structByIndex[i + 1] ?? const ({}, null)] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -77,8 +76,8 @@ class _XmpCardState extends State<XmpCard> {
|
||||||
valueListenable: _indexNotifier,
|
valueListenable: _indexNotifier,
|
||||||
builder: (context, index, child) {
|
builder: (context, index, child) {
|
||||||
final data = _isIndexed ? indexedStructs![index] : widget.directStruct!;
|
final data = _isIndexed ? indexedStructs![index] : widget.directStruct!;
|
||||||
final props = data.item1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort();
|
final props = data.$1.entries.map((kv) => XmpProp(kv.key, kv.value.value)).toList()..sort();
|
||||||
final cards = data.item2;
|
final cards = data.$2;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -139,7 +138,7 @@ class _XmpCardState extends State<XmpCard> {
|
||||||
title: card.title,
|
title: card.title,
|
||||||
structByIndex: card.data,
|
structByIndex: card.data,
|
||||||
formatValue: widget.formatValue,
|
formatValue: widget.formatValue,
|
||||||
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.item1) : null,
|
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.$1) : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -19,7 +19,6 @@ import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class XmpNamespace extends Equatable {
|
class XmpNamespace extends Equatable {
|
||||||
|
@ -114,7 +113,7 @@ class XmpNamespace extends Equatable {
|
||||||
title: card.title,
|
title: card.title,
|
||||||
structByIndex: card.data,
|
structByIndex: card.data,
|
||||||
formatValue: formatValue,
|
formatValue: formatValue,
|
||||||
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.item1) : null,
|
spanBuilders: spanBuilders != null ? (index) => spanBuilders(index, card.data[index]!.$1) : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -206,8 +205,8 @@ class XmpCardData {
|
||||||
final match = matches.first;
|
final match = matches.first;
|
||||||
final field = match.group(1)!;
|
final field = match.group(1)!;
|
||||||
|
|
||||||
final fields = data.putIfAbsent(null, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
|
final fields = data.putIfAbsent(null, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
|
||||||
final _cards = fields.item2;
|
final _cards = fields.$2;
|
||||||
if (_cards != null) {
|
if (_cards != null) {
|
||||||
final fieldProp = XmpProp(field, prop.value);
|
final fieldProp = XmpProp(field, prop.value);
|
||||||
if (_cards.any((v) => v.extract(fieldProp))) {
|
if (_cards.any((v) => v.extract(fieldProp))) {
|
||||||
|
@ -215,7 +214,7 @@ class XmpCardData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.item1[field] = prop;
|
fields.$1[field] = prop;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +226,8 @@ class XmpCardData {
|
||||||
final index = int.parse(match.group(1)!);
|
final index = int.parse(match.group(1)!);
|
||||||
final field = match.group(2)!;
|
final field = match.group(2)!;
|
||||||
|
|
||||||
final fields = data.putIfAbsent(index, () => Tuple2(<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
|
final fields = data.putIfAbsent(index, () => (<String, XmpProp>{}, cards?.map((v) => v.cloneEmpty()).toList()));
|
||||||
final _cards = fields.item2;
|
final _cards = fields.$2;
|
||||||
if (_cards != null) {
|
if (_cards != null) {
|
||||||
final fieldProp = XmpProp(field, prop.value);
|
final fieldProp = XmpProp(field, prop.value);
|
||||||
if (_cards.any((v) => v.extract(fieldProp))) {
|
if (_cards.any((v) => v.extract(fieldProp))) {
|
||||||
|
@ -236,7 +235,7 @@ class XmpCardData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.item1[field] = prop;
|
fields.$1[field] = prop;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:aves/widgets/viewer/info/embedded/notifications.dart';
|
import 'package:aves/widgets/viewer/info/embedded/notifications.dart';
|
||||||
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
abstract class XmpGoogleNamespace extends XmpNamespace {
|
abstract class XmpGoogleNamespace extends XmpNamespace {
|
||||||
XmpGoogleNamespace({
|
XmpGoogleNamespace({
|
||||||
|
@ -13,13 +12,12 @@ abstract class XmpGoogleNamespace extends XmpNamespace {
|
||||||
required super.rawProps,
|
required super.rawProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
List<Tuple2<String, String>> get dataProps;
|
List<(String, String)> get dataProps;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, InfoValueSpanBuilder> linkifyValues(List<XmpProp> props) {
|
Map<String, InfoValueSpanBuilder> linkifyValues(List<XmpProp> props) {
|
||||||
return Map.fromEntries(dataProps.map((t) {
|
return Map.fromEntries(dataProps.map((t) {
|
||||||
final dataPropPath = t.item1;
|
final (dataPropPath, mimePropPath) = t;
|
||||||
final mimePropPath = t.item2;
|
|
||||||
final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath);
|
final dataProp = props.firstWhereOrNull((prop) => prop.path == dataPropPath);
|
||||||
final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath);
|
final mimeProp = props.firstWhereOrNull((prop) => prop.path == mimePropPath);
|
||||||
return (dataProp != null && mimeProp != null)
|
return (dataProp != null && mimeProp != null)
|
||||||
|
@ -60,8 +58,8 @@ class XmpGAudioNamespace extends XmpGoogleNamespace {
|
||||||
XmpGAudioNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gAudio);
|
XmpGAudioNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gAudio);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Tuple2<String, String>> get dataProps => [
|
List<(String, String)> get dataProps => [
|
||||||
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
|
('${nsPrefix}Data', '${nsPrefix}Mime'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +67,8 @@ class XmpGCameraNamespace extends XmpGoogleNamespace {
|
||||||
XmpGCameraNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gCamera);
|
XmpGCameraNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gCamera);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Tuple2<String, String>> get dataProps => [
|
List<(String, String)> get dataProps => [
|
||||||
Tuple2('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'),
|
('${nsPrefix}RelitInputImageData', '${nsPrefix}RelitInputImageMime'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,9 +85,9 @@ class XmpGDepthNamespace extends XmpGoogleNamespace {
|
||||||
XmpGDepthNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gDepth);
|
XmpGDepthNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gDepth);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Tuple2<String, String>> get dataProps => [
|
List<(String, String)> get dataProps => [
|
||||||
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
|
('${nsPrefix}Data', '${nsPrefix}Mime'),
|
||||||
Tuple2('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'),
|
('${nsPrefix}Confidence', '${nsPrefix}ConfidenceMime'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +154,7 @@ class XmpGImageNamespace extends XmpGoogleNamespace {
|
||||||
XmpGImageNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gImage);
|
XmpGImageNamespace({required super.schemaRegistryPrefixes, required super.rawProps}) : super(nsUri: XmpNamespaces.gImage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Tuple2<String, String>> get dataProps => [
|
List<(String, String)> get dataProps => [
|
||||||
Tuple2('${nsPrefix}Data', '${nsPrefix}Mime'),
|
('${nsPrefix}Data', '${nsPrefix}Mime'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class WallpaperButtons extends StatelessWidget with FeedbackMixin {
|
class WallpaperButtons extends StatelessWidget with FeedbackMixin {
|
||||||
final AvesEntry entry;
|
final AvesEntry entry;
|
||||||
|
@ -57,16 +56,13 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
|
||||||
|
|
||||||
Future<void> _setWallpaper(BuildContext context) async {
|
Future<void> _setWallpaper(BuildContext context) async {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final value = await showDialog<Tuple2<WallpaperTarget, bool>>(
|
final value = await showDialog<(WallpaperTarget, bool)>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const WallpaperSettingsDialog(),
|
builder: (context) => const WallpaperSettingsDialog(),
|
||||||
routeSettings: const RouteSettings(name: WallpaperSettingsDialog.routeName),
|
routeSettings: const RouteSettings(name: WallpaperSettingsDialog.routeName),
|
||||||
);
|
);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
|
|
||||||
final target = value.item1;
|
|
||||||
final useScrollEffect = value.item2;
|
|
||||||
|
|
||||||
final reportController = StreamController.broadcast();
|
final reportController = StreamController.broadcast();
|
||||||
unawaited(showOpReport(
|
unawaited(showOpReport(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -76,6 +72,7 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
|
||||||
var region = _getVisibleRegion(context);
|
var region = _getVisibleRegion(context);
|
||||||
if (region == null) return;
|
if (region == null) return;
|
||||||
|
|
||||||
|
final (target, useScrollEffect) = value;
|
||||||
if (useScrollEffect) {
|
if (useScrollEffect) {
|
||||||
final deltaX = min(region.left, entry.displaySize.width - region.right);
|
final deltaX = min(region.left, entry.displaySize.width - region.right);
|
||||||
region = Rect.fromLTRB(region.left - deltaX, region.top, region.right + deltaX, region.bottom);
|
region = Rect.fromLTRB(region.left - deltaX, region.top, region.right + deltaX, region.bottom);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class SlideshowPage extends StatefulWidget {
|
class SlideshowPage extends StatefulWidget {
|
||||||
static const routeName = '/collection/slideshow';
|
static const routeName = '/collection/slideshow';
|
||||||
|
@ -151,7 +150,7 @@ class _SlideshowPageState extends State<SlideshowPage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<bool, bool> get collectionSettings => Tuple2(settings.slideshowShuffle, settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip);
|
(bool, bool) get collectionSettings => (settings.slideshowShuffle, settings.slideshowVideoPlayback == SlideshowVideoPlayback.skip);
|
||||||
|
|
||||||
Future<void> _showSettings(BuildContext context) async {
|
Future<void> _showSettings(BuildContext context) async {
|
||||||
final oldCollectionSettings = collectionSettings;
|
final oldCollectionSettings = collectionSettings;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/entry/extensions/props.dart';
|
import 'package:aves/model/entry/extensions/props.dart';
|
||||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/model/view_state.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/media_session_service.dart';
|
import 'package:aves/services/media/media_session_service.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
|
@ -18,7 +19,6 @@ import 'package:aves/widgets/viewer/video/conductor.dart';
|
||||||
import 'package:aves/widgets/viewer/view/conductor.dart';
|
import 'package:aves/widgets/viewer/view/conductor.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/error.dart';
|
import 'package:aves/widgets/viewer/visual/error.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/raster.dart';
|
import 'package:aves/widgets/viewer/visual/raster.dart';
|
||||||
import 'package:aves/model/view_state.dart';
|
|
||||||
import 'package:aves/widgets/viewer/visual/vector.dart';
|
import 'package:aves/widgets/viewer/visual/vector.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/video/cover.dart';
|
import 'package:aves/widgets/viewer/visual/video/cover.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/video/subtitle/subtitle.dart';
|
import 'package:aves/widgets/viewer/visual/video/subtitle/subtitle.dart';
|
||||||
|
@ -29,7 +29,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:decorated_icon/decorated_icon.dart';
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class EntryPageView extends StatefulWidget {
|
class EntryPageView extends StatefulWidget {
|
||||||
final AvesEntry mainEntry, pageEntry;
|
final AvesEntry mainEntry, pageEntry;
|
||||||
|
@ -203,16 +202,14 @@ class _EntryPageViewState extends State<EntryPageView> with SingleTickerProvider
|
||||||
final videoDisplaySize = entry.videoDisplaySize(sar);
|
final videoDisplaySize = entry.videoDisplaySize(sar);
|
||||||
final isPureVideo = entry.isPureVideo;
|
final isPureVideo = entry.isPureVideo;
|
||||||
|
|
||||||
return Selector<Settings, Tuple3<bool, bool, bool>>(
|
return Selector<Settings, (bool, bool, bool)>(
|
||||||
selector: (context, s) => Tuple3(
|
selector: (context, s) => (
|
||||||
isPureVideo && s.videoGestureDoubleTapTogglePlay,
|
isPureVideo && s.videoGestureDoubleTapTogglePlay,
|
||||||
isPureVideo && s.videoGestureSideDoubleTapSeek,
|
isPureVideo && s.videoGestureSideDoubleTapSeek,
|
||||||
isPureVideo && s.videoGestureVerticalDragBrightnessVolume,
|
isPureVideo && s.videoGestureVerticalDragBrightnessVolume,
|
||||||
),
|
),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
final playGesture = s.item1;
|
final (playGesture, seekGesture, useVerticalDragGesture) = s;
|
||||||
final seekGesture = s.item2;
|
|
||||||
final useVerticalDragGesture = s.item3;
|
|
||||||
final useTapGesture = playGesture || seekGesture;
|
final useTapGesture = playGesture || seekGesture;
|
||||||
|
|
||||||
MagnifierDoubleTapCallback? onDoubleTap;
|
MagnifierDoubleTapCallback? onDoubleTap;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class RasterImageView extends StatefulWidget {
|
class RasterImageView extends StatefulWidget {
|
||||||
final AvesEntry entry;
|
final AvesEntry entry;
|
||||||
|
@ -259,10 +258,11 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
viewRect: viewRect,
|
viewRect: viewRect,
|
||||||
);
|
);
|
||||||
if (rects != null) {
|
if (rects != null) {
|
||||||
|
final (tileRect, regionRect) = rects;
|
||||||
tiles.add(_RegionTile(
|
tiles.add(_RegionTile(
|
||||||
entry: entry,
|
entry: entry,
|
||||||
tileRect: rects.item1,
|
tileRect: tileRect,
|
||||||
regionRect: rects.item2,
|
regionRect: regionRect,
|
||||||
sampleSize: sampleSize,
|
sampleSize: sampleSize,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
return viewOrigin & viewportSize;
|
return viewOrigin & viewportSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<Rect, Rectangle<int>>? _getTileRects({
|
(Rect tileRect, Rectangle<int> regionRect)? _getTileRects({
|
||||||
required int x,
|
required int x,
|
||||||
required int y,
|
required int y,
|
||||||
required int regionSide,
|
required int regionSide,
|
||||||
|
@ -314,7 +314,7 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
} else {
|
} else {
|
||||||
regionRect = Rectangle<int>(x, y, thisRegionWidth, thisRegionHeight);
|
regionRect = Rectangle<int>(x, y, thisRegionWidth, thisRegionHeight);
|
||||||
}
|
}
|
||||||
return Tuple2<Rect, Rectangle<int>>(tileRect, regionRect);
|
return (tileRect, regionRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:aves/widgets/viewer/visual/entry_page_view.dart';
|
||||||
import 'package:aves_model/aves_model.dart';
|
import 'package:aves_model/aves_model.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
class VectorImageView extends StatefulWidget {
|
class VectorImageView extends StatefulWidget {
|
||||||
final AvesEntry entry;
|
final AvesEntry entry;
|
||||||
|
@ -233,10 +232,11 @@ class _VectorImageViewState extends State<VectorImageView> {
|
||||||
viewRect: viewRect,
|
viewRect: viewRect,
|
||||||
);
|
);
|
||||||
if (rects != null) {
|
if (rects != null) {
|
||||||
|
final (tileRect, regionRect) = rects;
|
||||||
tiles.add(_RegionTile(
|
tiles.add(_RegionTile(
|
||||||
entry: entry,
|
entry: entry,
|
||||||
tileRect: rects.item1,
|
tileRect: tileRect,
|
||||||
regionRect: rects.item2,
|
regionRect: regionRect,
|
||||||
scale: svgScale,
|
scale: svgScale,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
backgroundFrameBuilder: backgroundFrameBuilder,
|
backgroundFrameBuilder: backgroundFrameBuilder,
|
||||||
|
@ -259,7 +259,7 @@ class _VectorImageViewState extends State<VectorImageView> {
|
||||||
return viewOrigin & viewportSize;
|
return viewOrigin & viewportSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple2<Rect, Rectangle<double>>? _getTileRects({
|
(Rect tileRect, Rectangle<double> regionRect)? _getTileRects({
|
||||||
required double x,
|
required double x,
|
||||||
required double y,
|
required double y,
|
||||||
required double regionSide,
|
required double regionSide,
|
||||||
|
@ -278,7 +278,7 @@ class _VectorImageViewState extends State<VectorImageView> {
|
||||||
if (!viewRect.overlaps(tileRect)) return null;
|
if (!viewRect.overlaps(tileRect)) return null;
|
||||||
|
|
||||||
final regionRect = Rectangle<double>(x, y, thisRegionWidth, thisRegionHeight);
|
final regionRect = Rectangle<double>(x, y, thisRegionWidth, thisRegionHeight);
|
||||||
return Tuple2<Rect, Rectangle<double>>(tileRect, regionRect);
|
return (tileRect, regionRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _imageScaleForViewScale({
|
double _imageScaleForViewScale({
|
||||||
|
|
|
@ -1479,14 +1479,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
tuple:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: tuple
|
|
||||||
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -111,7 +111,6 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/deckerst/aves_streams_channel.git
|
url: https://github.com/deckerst/aves_streams_channel.git
|
||||||
transparent_image:
|
transparent_image:
|
||||||
tuple:
|
|
||||||
url_launcher:
|
url_launcher:
|
||||||
vector_math:
|
vector_math:
|
||||||
volume_controller:
|
volume_controller:
|
||||||
|
|
Loading…
Reference in a new issue