This commit is contained in:
Thibault Deckers 2024-06-24 00:47:27 +02:00
parent 930cdf9120
commit ace841212e
40 changed files with 358 additions and 65 deletions

View file

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- Collection: stack RAW and JPEG with same file names
- Collection: ask to rename/replace/skip when converting items with name conflict
- Export: bulk converting motion photos to still images
- Explorer: view folder tree and filter paths
### Fixed

View file

@ -771,6 +771,8 @@
"binPageTitle": "Recycle Bin",
"explorerPageTitle": "Explorer",
"searchCollectionFieldHint": "Search collection",
"searchRecentSectionTitle": "Recent",
"searchDateSectionTitle": "Date",

View file

@ -52,13 +52,13 @@ class AlbumFilter extends CoveredCollectionFilter {
String getTooltip(BuildContext context) => album;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
return IconUtils.getAlbumIcon(
context: context,
albumPath: album,
size: size,
) ??
(showGenericIcon ? Icon(AIcons.album, size: size) : null);
(allowGenericIcon ? Icon(AIcons.album, size: size) : null);
}
@override

View file

@ -68,7 +68,7 @@ class AspectRatioFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.aspectRatio, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.aspectRatio, size: size);
@override
String get category => type;

View file

@ -69,7 +69,7 @@ class CoordinateFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.geoBounds, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.geoBounds, size: size);
@override
String get category => type;

View file

@ -122,7 +122,7 @@ class DateFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.date, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.date, size: size);
@override
String get category => type;

View file

@ -45,7 +45,7 @@ class FavouriteFilter extends CollectionFilter {
String getLabel(BuildContext context) => context.l10n.filterFavouriteLabel;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.favourite, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.favourite, size: size);
@override
Future<Color> color(BuildContext context) {

View file

@ -133,7 +133,7 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
String getTooltip(BuildContext context) => getLabel(context);
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => null;
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => null;
Future<Color> color(BuildContext context) {
final colors = context.read<AvesColorsData>();

View file

@ -89,7 +89,7 @@ class LocationFilter extends CoveredCollectionFilter {
String getLabel(BuildContext context) => _isUnlocated ? context.l10n.filterNoLocationLabel : _location;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
if (_isUnlocated) {
return Icon(AIcons.locationUnlocated, size: size);
}

View file

@ -77,7 +77,7 @@ class MimeFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(_icon, size: size);
@override
Future<Color> color(BuildContext context) {

View file

@ -70,7 +70,7 @@ class MissingFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(_icon, size: size);
@override
String get category => type;

View file

@ -60,8 +60,8 @@ class OrFilter extends CollectionFilter {
String getLabel(BuildContext context) => _filters.map((v) => v.getLabel(context)).join(', ');
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
return _genericIcon != null ? Icon(_genericIcon, size: size) : _first.iconBuilder(context, size, showGenericIcon: showGenericIcon);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
return _genericIcon != null ? Icon(_genericIcon, size: size) : _first.iconBuilder(context, size, allowGenericIcon: allowGenericIcon);
}
@override

View file

@ -1,5 +1,9 @@
import 'package:aves/model/filters/filters.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/view/view.dart';
import 'package:flutter/widgets.dart';
class PathFilter extends CollectionFilter {
static const type = 'path';
@ -47,6 +51,19 @@ class PathFilter extends CollectionFilter {
@override
String get universalLabel => path;
@override
String getLabel(BuildContext context) {
final _directory = androidFileUtils.relativeDirectoryFromPath(path);
if (_directory == null) return universalLabel;
if (_directory.relativeDir.isEmpty) {
return _directory.getVolumeDescription(context);
}
return pContext.split(_directory.relativeDir).last;
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.explorer, size: size);
@override
String get category => type;

View file

@ -96,7 +96,7 @@ class PlaceholderFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(_icon, size: size);
@override
String get category => type;

View file

@ -82,7 +82,7 @@ class QueryFilter extends CollectionFilter {
String get universalLabel => query;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.text, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.text, size: size);
@override
Future<Color> color(BuildContext context) {

View file

@ -64,7 +64,7 @@ class RatingFilter extends CollectionFilter {
};
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
return switch (rating) {
-1 => Icon(AIcons.ratingRejected, size: size),
0 => Icon(AIcons.ratingUnrated, size: size),

View file

@ -51,7 +51,7 @@ class RecentlyAddedFilter extends CollectionFilter {
String getLabel(BuildContext context) => context.l10n.filterRecentlyAddedLabel;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.dateRecent, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.dateRecent, size: size);
@override
String get category => type;

View file

@ -47,8 +47,8 @@ class TagFilter extends CoveredCollectionFilter {
String getLabel(BuildContext context) => tag.isEmpty ? context.l10n.filterNoTagLabel : tag;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
return showGenericIcon ? Icon(tag.isEmpty ? AIcons.tagUntagged : AIcons.tag, size: size) : null;
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) {
return allowGenericIcon ? Icon(tag.isEmpty ? AIcons.tagUntagged : AIcons.tag, size: size) : null;
}
@override

View file

@ -41,7 +41,7 @@ class TrashFilter extends CollectionFilter {
String getLabel(BuildContext context) => context.l10n.filterBinLabel;
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.bin, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(AIcons.bin, size: size);
@override
String get category => type;

View file

@ -99,7 +99,7 @@ class TypeFilter extends CollectionFilter {
}
@override
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) => Icon(_icon, size: size);
@override
Future<Color> color(BuildContext context) {

View file

@ -1,4 +1,5 @@
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves_model/aves_model.dart';
@ -12,6 +13,8 @@ extension ExtraHomePageSetting on HomePageSetting {
return AlbumListPage.routeName;
case HomePageSetting.tags:
return TagListPage.routeName;
case HomePageSetting.explorer:
return ExplorerPage.routeName;
}
}
}

View file

@ -29,6 +29,7 @@ class AIcons {
static const disc = Icons.fiber_manual_record;
static const display = Icons.light_mode_outlined;
static const error = Icons.error_outline;
static const explorer = Icons.account_tree_outlined;
static const folder = Icons.folder_outlined;
static const geoBounds = Icons.public_outlined;
static final github = MdiIcons.github;

View file

@ -83,6 +83,7 @@ extension ExtraHomePageSettingView on HomePageSetting {
HomePageSetting.collection => l10n.drawerCollectionAll,
HomePageSetting.albums => l10n.drawerAlbumPage,
HomePageSetting.tags => l10n.drawerTagPage,
HomePageSetting.explorer => l10n.explorerPageTitle,
};
}
}

View file

@ -7,6 +7,7 @@ export 'src/actions/map_cluster.dart';
export 'src/actions/share.dart';
export 'src/actions/slideshow.dart';
export 'src/editor/enums.dart';
export 'src/metadata/convert_action.dart';
export 'src/metadata/date_edit_action.dart';
export 'src/metadata/date_field_source.dart';
export 'src/metadata/fields.dart';

View file

@ -35,7 +35,7 @@ class AlbumQuickChooser extends StatelessWidget {
pointerGlobalPosition: pointerGlobalPosition,
itemBuilder: (context, album) => AvesFilterChip(
filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)),
showGenericIcon: false,
allowGenericIcon: false,
),
);
}

View file

@ -32,7 +32,7 @@ class TagQuickChooser extends StatelessWidget {
pointerGlobalPosition: pointerGlobalPosition,
itemBuilder: (context, filter) => AvesFilterChip(
filter: filter,
showGenericIcon: false,
allowGenericIcon: false,
),
);
}

View file

@ -4,6 +4,7 @@ import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves_model/aves_model.dart';
@ -32,7 +33,7 @@ class TvNavigationPopHandler {
return switch (homePage) {
HomePageSetting.collection => context.read<CollectionLens>().filters.isEmpty,
HomePageSetting.albums || HomePageSetting.tags => true,
HomePageSetting.albums || HomePageSetting.tags || HomePageSetting.explorer => true,
};
}
@ -47,6 +48,7 @@ class TvNavigationPopHandler {
HomePageSetting.collection => buildRoute((context) => CollectionPage(source: context.read<CollectionSource>(), filters: null)),
HomePageSetting.albums => buildRoute((context) => const AlbumListPage()),
HomePageSetting.tags => buildRoute((context) => const TagListPage()),
HomePageSetting.explorer => buildRoute((context) => const ExplorerPage()),
};
}
}

View file

@ -170,7 +170,7 @@ class ExpandableFilterRow extends StatelessWidget {
// key `album-{path}` is expected by test driver
key: Key(filter.key),
filter: filter,
showGenericIcon: showGenericIcon,
allowGenericIcon: showGenericIcon,
leadingOverride: leadingBuilder?.call(filter),
heroType: heroTypeBuilder?.call(filter) ?? HeroType.onTap,
onTap: onTap,

View file

@ -47,7 +47,7 @@ class AvesFilterDecoration {
class AvesFilterChip extends StatefulWidget {
final CollectionFilter filter;
final bool showText, showGenericIcon, useFilterColor;
final bool showLeading, showText, allowGenericIcon, useFilterColor;
final AvesFilterDecoration? decoration;
final Color? background;
final String? banner;
@ -61,7 +61,7 @@ class AvesFilterChip extends StatefulWidget {
static const double defaultRadius = 32;
static const double outlineWidth = 2;
static const double minChipHeight = kMinInteractiveDimension;
static const double minChipWidth = 80;
static const double minChipWidth = kMinInteractiveDimension;
static const double iconSize = 18;
static const double fontSize = 14;
static const double decoratedContentVerticalPadding = 5;
@ -69,8 +69,9 @@ class AvesFilterChip extends StatefulWidget {
const AvesFilterChip({
super.key,
required this.filter,
this.showLeading = true,
this.showText = true,
this.showGenericIcon = true,
this.allowGenericIcon = true,
this.useFilterColor = true,
this.decoration,
this.background,
@ -255,10 +256,12 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
: null;
Widget? content;
if (widget.showText) {
final showLeading = widget.showLeading;
final showText = widget.showText;
if (showLeading || showText) {
final textScaler = MediaQuery.textScalerOf(context);
final iconSize = textScaler.scale(AvesFilterChip.iconSize);
final leading = widget.leadingOverride ?? filter.iconBuilder(context, iconSize, showGenericIcon: widget.showGenericIcon);
final leading = showLeading ? widget.leadingOverride ?? filter.iconBuilder(context, iconSize, allowGenericIcon: widget.allowGenericIcon) : null;
final trailing = onRemove != null
? Theme(
data: Theme.of(context).copyWith(
@ -278,22 +281,21 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
mainAxisSize: decoration != null ? MainAxisSize.max : MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (leading != null) ...[
leading,
SizedBox(width: padding),
],
Flexible(
child: Text(
filter.getLabel(context),
style: TextStyle(
fontSize: AvesFilterChip.fontSize,
decoration: filter.reversed ? TextDecoration.lineThrough : null,
decorationThickness: 2,
if (leading != null) leading,
if (leading != null && showText) SizedBox(width: padding),
if (showText)
Flexible(
child: Text(
filter.getLabel(context),
style: TextStyle(
fontSize: AvesFilterChip.fontSize,
decoration: filter.reversed ? TextDecoration.lineThrough : null,
decorationThickness: 2,
),
softWrap: false,
overflow: TextOverflow.fade,
),
softWrap: false,
overflow: TextOverflow.fade,
),
),
if (trailing != null) ...[
SizedBox(width: padding),
trailing,

View file

@ -8,7 +8,6 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/utils/mime_utils.dart';
import 'package:aves/view/src/metadata/convert_action.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/list_tiles/slider.dart';
import 'package:aves/widgets/common/basic/text/change_highlight.dart';

View file

@ -6,7 +6,7 @@ import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/styles.dart';
import 'package:aves/view/src/metadata/fields.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/collection/collection_grid.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/expansion_panel.dart';
@ -122,11 +122,10 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
...[
MetadataField.exifMake,
MetadataField.exifModel,
]
.map((field) => PopupMenuItem(
value: MetadataFieldNamingProcessor.keyWithField(field),
child: MenuRow(text: field.title),
)),
].map((field) => PopupMenuItem(
value: MetadataFieldNamingProcessor.keyWithField(field),
child: MenuRow(text: field.title),
)),
PopupMenuItem(
value: HashNamingProcessor.key,
child: MenuRow(text: l10n.renameProcessorHash),

View file

@ -0,0 +1,250 @@
import 'dart:io';
import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/path.dart';
import 'package:aves/model/settings/enums/accessibility_animations.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/navigation/drawer/app_drawer.dart';
import 'package:aves/widgets/search/search_delegate.dart';
import 'package:aves/widgets/settings/privacy/file_picker/crumb_line.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
class ExplorerPage extends StatefulWidget {
static const routeName = '/explorer';
const ExplorerPage({super.key});
@override
State<ExplorerPage> createState() => _ExplorerPageState();
}
class _ExplorerPageState extends State<ExplorerPage> {
late VolumeRelativeDirectory _directory;
List<Directory>? _contents;
Set<StorageVolume> get volumes => androidFileUtils.storageVolumes;
String get currentDirectoryPath => pContext.join(_directory.volumePath, _directory.relativeDir);
@override
void initState() {
super.initState();
final primaryVolume = volumes.firstWhereOrNull((v) => v.isPrimary);
if (primaryVolume != null) {
_goTo(primaryVolume.path);
}
}
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final visibleContents = _contents?.where((v) {
final isHidden = pContext.split(v.path).last.startsWith('.');
return !isHidden;
}).toList();
return PopScope(
canPop: _directory.relativeDir.isEmpty,
onPopInvoked: (didPop) {
if (didPop) return;
final parent = pContext.dirname(currentDirectoryPath);
_goTo(parent);
setState(() {});
},
child: AvesScaffold(
appBar: _buildAppBar(context),
drawer: const AppDrawer(),
body: SafeArea(
child: Column(
children: [
SizedBox(
height: kMinInteractiveDimension,
child: CrumbLine(
directory: _directory,
onTap: (path) {
_goTo(path);
setState(() {});
},
),
),
const Divider(height: 0),
Expanded(
child: visibleContents == null
? const SizedBox()
: visibleContents.isEmpty
? Center(
child: EmptyContent(
icon: AIcons.folder,
text: l10n.filePickerNoItems,
),
)
: ListView.builder(
itemCount: visibleContents.length,
itemBuilder: (context, index) {
return index < visibleContents.length ? _buildContentLine(context, visibleContents[index]) : const SizedBox();
},
),
),
const Divider(height: 0),
Padding(
padding: const EdgeInsets.all(8),
child: AvesFilterChip(
filter: PathFilter(currentDirectoryPath),
maxWidth: double.infinity,
onTap: (filter) => _goToCollectionPage(context, filter),
onLongPress: null,
),
),
],
),
),
),
);
}
AppBar _buildAppBar(BuildContext context) {
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
return AppBar(
title: InteractiveAppBarTitle(
onTap: _goToSearch,
child: Text(
context.l10n.explorerPageTitle,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
),
),
actions: [
if (volumes.length > 1)
FontSizeIconTheme(
child: PopupMenuButton<StorageVolume>(
itemBuilder: (context) {
return volumes.map((v) {
final selected = _directory.volumePath == v.path;
final icon = v.isRemovable ? AIcons.storageCard : AIcons.storageMain;
return PopupMenuItem(
value: v,
enabled: !selected,
child: MenuRow(
text: v.getDescription(context),
icon: Icon(icon),
),
);
}).toList();
},
onSelected: (volume) async {
// wait for the popup menu to hide before proceeding with the action
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
Navigator.maybeOf(context)?.pop();
await Future.delayed(ADurations.drawerTransitionAnimation);
_goTo(volume.path);
setState(() {});
},
popUpAnimationStyle: animations.popUpAnimationStyle,
),
),
],
);
}
String? _getAlbumPath(CollectionSource source, FileSystemEntity content) {
final contentPath = content.path.toLowerCase();
return source.rawAlbums.firstWhereOrNull((v) => v.toLowerCase() == contentPath);
}
Widget _buildContentLine(BuildContext context, FileSystemEntity content) {
final source = context.read<CollectionSource>();
final album = _getAlbumPath(source, content);
final baseIconTheme = IconTheme.of(context);
return ListTile(
leading: const Icon(AIcons.folder),
title: Text('${Unicode.FSI}${pContext.split(content.path).last}${Unicode.PDI}'),
trailing: album != null
? IconTheme.merge(
data: baseIconTheme,
child: AvesFilterChip(
filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)),
showText: false,
maxWidth: AvesFilterChip.minChipWidth,
onTap: (filter) => _goToCollectionPage(context, filter),
onLongPress: null,
),
)
: null,
onTap: () {
_goTo(content.path);
setState(() {});
},
);
}
void _goTo(String path) {
_directory = androidFileUtils.relativeDirectoryFromPath(path)!;
_contents = null;
final contents = <Directory>[];
final source = context.read<CollectionSource>();
final albums = source.rawAlbums.map((v) => v.toLowerCase()).toSet();
Directory(currentDirectoryPath).list().listen((event) {
final entity = event.absolute;
if (entity is Directory) {
final dirPath = entity.path.toLowerCase();
if (albums.any((v) => v.startsWith(dirPath))) {
contents.add(entity);
}
}
}, onDone: () {
_contents = contents..sort((a, b) => compareAsciiUpperCaseNatural(pContext.split(a.path).last, pContext.split(b.path).last));
setState(() {});
});
}
void _goToSearch() {
Navigator.maybeOf(context)?.push(
SearchPageRoute(
delegate: CollectionSearchDelegate(
searchFieldLabel: context.l10n.searchCollectionFieldHint,
searchFieldStyle: Themes.searchFieldStyle(context),
source: context.read<CollectionSource>(),
),
),
);
}
void _goToCollectionPage(BuildContext context, CollectionFilter filter) {
Navigator.maybeOf(context)?.push(
MaterialPageRoute(
settings: const RouteSettings(name: CollectionPage.routeName),
builder: (context) => CollectionPage(
source: context.read<CollectionSource>(),
filters: {filter},
),
),
);
}
}

View file

@ -115,8 +115,9 @@ class CoveredFilterChip<T extends CollectionFilter> extends StatelessWidget {
return AvesFilterChip(
key: chipKey,
filter: _filter,
showLeading: showText,
showText: showText,
showGenericIcon: false,
allowGenericIcon: false,
decoration: AvesFilterDecoration(
radius: radius(extent),
widget: Padding(

View file

@ -33,7 +33,7 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
Widget build(BuildContext context) {
final detailsTheme = context.watch<FilterListDetailsThemeData>();
final leading = filter.iconBuilder(context, detailsTheme.titleIconSize, showGenericIcon: false);
final leading = filter.iconBuilder(context, detailsTheme.titleIconSize, allowGenericIcon: false);
final hasTitleLeading = leading != null;
return Container(

View file

@ -25,6 +25,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/page.dart';
import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/editor/entry_editor_page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
import 'package:aves/widgets/intent.dart';
@ -346,12 +347,14 @@ class _HomePageState extends State<HomePage> {
return buildRoute((context) => const AlbumListPage());
case TagListPage.routeName:
return buildRoute((context) => const TagListPage());
case ExplorerPage.routeName:
return buildRoute((context) => const ExplorerPage());
case HomeWidgetSettingsPage.routeName:
return buildRoute((context) => HomeWidgetSettingsPage(widgetId: _widgetId!));
case ScreenSaverPage.routeName:
return buildRoute((context) => ScreenSaverPage(source: source));
case ScreenSaverSettingsPage.routeName:
return buildRoute((context) => const ScreenSaverSettingsPage());
case HomeWidgetSettingsPage.routeName:
return buildRoute((context) => HomeWidgetSettingsPage(widgetId: _widgetId!));
case SearchPage.routeName:
return SearchPageRoute(
delegate: CollectionSearchDelegate(

View file

@ -6,6 +6,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/page.dart';
import 'package:aves/widgets/common/search/route.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/places_page.dart';
@ -95,12 +96,14 @@ class PageNavTile extends StatelessWidget {
return (_) => const PlaceListPage();
case TagListPage.routeName:
return (_) => const TagListPage();
case SettingsPage.routeName:
return (_) => const SettingsPage();
case AboutPage.routeName:
return (_) => const AboutPage();
case AppDebugPage.routeName:
return (_) => const AppDebugPage();
case ExplorerPage.routeName:
return (_) => const ExplorerPage();
case SettingsPage.routeName:
return (_) => const SettingsPage();
default:
throw Exception('unknown route=$routeName');
}

View file

@ -7,6 +7,7 @@ import 'package:aves/widgets/about/about_page.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/page.dart';
import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/places_page.dart';
@ -40,14 +41,16 @@ class NavigationDisplay {
return l10n.drawerPlacePage;
case TagListPage.routeName:
return l10n.drawerTagPage;
case SettingsPage.routeName:
return l10n.settingsPageTitle;
case AboutPage.routeName:
return l10n.aboutPageTitle;
case SearchPage.routeName:
return MaterialLocalizations.of(context).searchFieldLabel;
case AppDebugPage.routeName:
return 'Debug';
case ExplorerPage.routeName:
return l10n.explorerPageTitle;
case SearchPage.routeName:
return MaterialLocalizations.of(context).searchFieldLabel;
case SettingsPage.routeName:
return l10n.settingsPageTitle;
default:
return route;
}
@ -63,14 +66,16 @@ class NavigationDisplay {
return AIcons.place;
case TagListPage.routeName:
return AIcons.tag;
case SettingsPage.routeName:
return AIcons.settings;
case AboutPage.routeName:
return AIcons.info;
case SearchPage.routeName:
return AIcons.search;
case AppDebugPage.routeName:
return AIcons.debug;
case ExplorerPage.routeName:
return AIcons.explorer;
case SearchPage.routeName:
return AIcons.search;
case SettingsPage.routeName:
return AIcons.settings;
default:
return null;
}

View file

@ -4,6 +4,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/search/page.dart';
import 'package:aves/widgets/explorer/explorer_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/places_page.dart';
@ -41,6 +42,7 @@ class _NavigationDrawerEditorPageState extends State<NavigationDrawerEditorPage>
CountryListPage.routeName,
PlaceListPage.routeName,
TagListPage.routeName,
ExplorerPage.routeName,
SearchPage.routeName,
};

View file

@ -73,6 +73,7 @@ class SettingsTileNavigationHomePage extends SettingsTile {
const _HomeOption(HomePageSetting.collection),
const _HomeOption(HomePageSetting.albums),
const _HomeOption(HomePageSetting.tags),
const _HomeOption(HomePageSetting.explorer),
if (settings.homeCustomCollection.isNotEmpty) _HomeOption(HomePageSetting.collection, customCollection: settings.homeCustomCollection),
],
getName: (context, v) => v.getName(context),

View file

@ -14,7 +14,7 @@ enum DisplayRefreshRateMode { auto, highest, lowest }
enum EntryBackground { black, white, checkered }
enum HomePageSetting { collection, albums, tags }
enum HomePageSetting { collection, albums, tags, explorer }
enum KeepScreenOn { never, videoPlayback, viewerOnly, always }