From c0af01578a346d9b8dba9f82cb357ce95bf7dd88 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Wed, 18 Aug 2021 13:24:43 +0900 Subject: [PATCH] menu fixes --- lib/model/actions/chip_actions.dart | 6 +- lib/model/actions/chip_set_actions.dart | 6 +- lib/model/actions/entry_actions.dart | 19 ++- ...on_actions.dart => entry_set_actions.dart} | 54 ++++--- lib/model/actions/video_actions.dart | 6 +- lib/theme/themes.dart | 9 ++ lib/widgets/collection/app_bar.dart | 145 +++++++++--------- .../collection/entry_set_action_delegate.dart | 14 +- lib/widgets/common/basic/menu.dart | 47 ++++++ lib/widgets/common/basic/menu_row.dart | 37 ----- .../common/identity/aves_filter_chip.dart | 6 +- lib/widgets/drawer/tile.dart | 6 +- lib/widgets/filter_grids/album_pick.dart | 44 +++--- .../common/action_delegates/album_set.dart | 1 + .../common/action_delegates/chip_set.dart | 1 + lib/widgets/filter_grids/common/app_bar.dart | 115 +++++++------- .../common/quick_actions/action_button.dart | 4 +- .../quick_actions/available_actions.dart | 2 +- .../common/quick_actions/editor_page.dart | 4 +- lib/widgets/settings/settings_page.dart | 38 ++--- lib/widgets/viewer/overlay/bottom/video.dart | 24 +-- lib/widgets/viewer/overlay/top.dart | 42 ++--- 22 files changed, 348 insertions(+), 282 deletions(-) rename lib/model/actions/{collection_actions.dart => entry_set_actions.dart} (61%) create mode 100644 lib/widgets/common/basic/menu.dart delete mode 100644 lib/widgets/common/basic/menu_row.dart diff --git a/lib/model/actions/chip_actions.dart b/lib/model/actions/chip_actions.dart index afc7e6721..046850c81 100644 --- a/lib/model/actions/chip_actions.dart +++ b/lib/model/actions/chip_actions.dart @@ -23,7 +23,11 @@ extension ExtraChipAction on ChipAction { } } - IconData getIcon() { + Widget getIcon() { + return Icon(_getIconData()); + } + + IconData _getIconData() { switch (this) { case ChipAction.goToAlbumPage: return AIcons.album; diff --git a/lib/model/actions/chip_set_actions.dart b/lib/model/actions/chip_set_actions.dart index b0dedc1c8..83a43d636 100644 --- a/lib/model/actions/chip_set_actions.dart +++ b/lib/model/actions/chip_set_actions.dart @@ -60,7 +60,11 @@ extension ExtraChipSetAction on ChipSetAction { } } - IconData? getIcon() { + Widget getIcon() { + return Icon(_getIconData()); + } + + IconData _getIconData() { switch (this) { // general case ChipSetAction.sort: diff --git a/lib/model/actions/entry_actions.dart b/lib/model/actions/entry_actions.dart index f54eecf70..ceb1b528e 100644 --- a/lib/model/actions/entry_actions.dart +++ b/lib/model/actions/entry_actions.dart @@ -1,4 +1,5 @@ import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; @@ -115,7 +116,23 @@ extension ExtraEntryAction on EntryAction { } } - IconData? getIcon() { + Widget? getIcon() { + final icon = getIconData(); + if (icon == null) return null; + + final child = Icon(icon); + switch (this) { + case EntryAction.debug: + return ShaderMask( + shaderCallback: Themes.debugGradient.createShader, + child: child, + ); + default: + return child; + } + } + + IconData? getIconData() { switch (this) { case EntryAction.toggleFavourite: // different data depending on toggle state diff --git a/lib/model/actions/collection_actions.dart b/lib/model/actions/entry_set_actions.dart similarity index 61% rename from lib/model/actions/collection_actions.dart rename to lib/model/actions/entry_set_actions.dart index 27303390e..aa8cb1810 100644 --- a/lib/model/actions/collection_actions.dart +++ b/lib/model/actions/entry_set_actions.dart @@ -2,7 +2,7 @@ import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; -enum CollectionAction { +enum EntrySetAction { // general sort, group, @@ -20,65 +20,69 @@ enum CollectionAction { refreshMetadata, } -extension ExtraCollectionAction on CollectionAction { +extension ExtraEntrySetAction on EntrySetAction { String getText(BuildContext context) { switch (this) { // general - case CollectionAction.sort: + case EntrySetAction.sort: return context.l10n.menuActionSort; - case CollectionAction.group: + case EntrySetAction.group: return context.l10n.menuActionGroup; - case CollectionAction.select: + case EntrySetAction.select: return context.l10n.menuActionSelect; - case CollectionAction.selectAll: + case EntrySetAction.selectAll: return context.l10n.menuActionSelectAll; - case CollectionAction.selectNone: + case EntrySetAction.selectNone: return context.l10n.menuActionSelectNone; // all - case CollectionAction.addShortcut: + case EntrySetAction.addShortcut: return context.l10n.collectionActionAddShortcut; // all or entry selection - case CollectionAction.map: + case EntrySetAction.map: return context.l10n.menuActionMap; - case CollectionAction.stats: + case EntrySetAction.stats: return context.l10n.menuActionStats; // entry selection - case CollectionAction.copy: + case EntrySetAction.copy: return context.l10n.collectionActionCopy; - case CollectionAction.move: + case EntrySetAction.move: return context.l10n.collectionActionMove; - case CollectionAction.refreshMetadata: + case EntrySetAction.refreshMetadata: return context.l10n.collectionActionRefreshMetadata; } } - IconData? getIcon() { + Widget getIcon() { + return Icon(_getIconData()); + } + + IconData _getIconData() { switch (this) { // general - case CollectionAction.sort: + case EntrySetAction.sort: return AIcons.sort; - case CollectionAction.group: + case EntrySetAction.group: return AIcons.group; - case CollectionAction.select: + case EntrySetAction.select: return AIcons.select; - case CollectionAction.selectAll: + case EntrySetAction.selectAll: return AIcons.selected; - case CollectionAction.selectNone: + case EntrySetAction.selectNone: return AIcons.unselected; // all - case CollectionAction.addShortcut: + case EntrySetAction.addShortcut: return AIcons.addShortcut; // all or entry selection - case CollectionAction.map: + case EntrySetAction.map: return AIcons.map; - case CollectionAction.stats: + case EntrySetAction.stats: return AIcons.stats; // entry selection - case CollectionAction.copy: + case EntrySetAction.copy: return AIcons.copy; - case CollectionAction.move: + case EntrySetAction.move: return AIcons.move; - case CollectionAction.refreshMetadata: + case EntrySetAction.refreshMetadata: return AIcons.refresh; } } diff --git a/lib/model/actions/video_actions.dart b/lib/model/actions/video_actions.dart index d4de71750..dcddaf60b 100644 --- a/lib/model/actions/video_actions.dart +++ b/lib/model/actions/video_actions.dart @@ -46,7 +46,11 @@ extension ExtraVideoAction on VideoAction { } } - IconData? getIcon() { + Widget getIcon() { + return Icon(_getIconData()); + } + + IconData _getIconData() { switch (this) { case VideoAction.captureFrame: return AIcons.captureFrame; diff --git a/lib/theme/themes.dart b/lib/theme/themes.dart index df131dcdb..73f4c2ad4 100644 --- a/lib/theme/themes.dart +++ b/lib/theme/themes.dart @@ -6,6 +6,15 @@ import 'package:flutter/services.dart'; class Themes { static const _accentColor = Colors.indigoAccent; + static const debugGradient = LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.red, + Colors.amber, + ], + ); + static final darkTheme = ThemeData( brightness: Brightness.dark, accentColor: _accentColor, diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 6fe096a77..0f364a23c 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; -import 'package:aves/model/actions/collection_actions.dart'; import 'package:aves/model/actions/entry_actions.dart'; +import 'package:aves/model/actions/entry_set_actions.dart'; import 'package:aves/model/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/selection.dart'; @@ -17,7 +17,7 @@ import 'package:aves/widgets/collection/entry_set_action_delegate.dart'; import 'package:aves/widgets/collection/filter_bar.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar_title.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart'; import 'package:aves/widgets/dialogs/aves_selection_dialog.dart'; @@ -177,7 +177,7 @@ class _CollectionAppBarState extends State with SingleTickerPr ...EntryActions.selection.map((action) => Selector, bool>( selector: (context, selection) => selection.selectedItems.isEmpty, builder: (context, isEmpty, child) => IconButton( - icon: Icon(action.getIcon()), + icon: action.getIcon() ?? const SizedBox(), onPressed: isEmpty ? null : () => _actionDelegate.onEntryActionSelected(context, action), tooltip: action.getText(context), ), @@ -186,81 +186,80 @@ class _CollectionAppBarState extends State with SingleTickerPr future: _canAddShortcutsLoader, builder: (context, snapshot) { final canAddShortcuts = snapshot.data ?? false; - return PopupMenuButton( - key: const Key('appbar-menu-button'), - itemBuilder: (context) { - final groupable = collection.sortFactor == EntrySortFactor.date; - final selection = context.read>(); - final isSelecting = selection.isSelecting; - final selectedItems = selection.selectedItems; - final hasSelection = selectedItems.isNotEmpty; - final hasItems = !collection.isEmpty; - final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection); + return MenuIconTheme( + child: PopupMenuButton( + key: const Key('appbar-menu-button'), + itemBuilder: (context) { + final groupable = collection.sortFactor == EntrySortFactor.date; + final selection = context.read>(); + final isSelecting = selection.isSelecting; + final selectedItems = selection.selectedItems; + final hasSelection = selectedItems.isNotEmpty; + final hasItems = !collection.isEmpty; + final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection); - return [ - _toMenuItem( - CollectionAction.sort, - key: const Key('menu-sort'), - ), - if (groupable) + return [ _toMenuItem( - CollectionAction.group, - key: const Key('menu-group'), + EntrySetAction.sort, + key: const Key('menu-sort'), ), - if (appMode == AppMode.main) ...[ - if (!isSelecting) + if (groupable) _toMenuItem( - CollectionAction.select, - enabled: hasItems, + EntrySetAction.group, + key: const Key('menu-group'), ), - const PopupMenuDivider(), - if (isSelecting) - ...[ - CollectionAction.copy, - CollectionAction.move, - CollectionAction.refreshMetadata, - ].map((v) => _toMenuItem(v, enabled: hasSelection)), - ...[ - CollectionAction.map, - CollectionAction.stats, - ].map((v) => _toMenuItem(v, enabled: otherViewEnabled)), - if (!isSelecting && canAddShortcuts) ...[ + if (appMode == AppMode.main) ...[ + if (!isSelecting) + _toMenuItem( + EntrySetAction.select, + enabled: hasItems, + ), const PopupMenuDivider(), - _toMenuItem(CollectionAction.addShortcut), + if (isSelecting) + ...[ + EntrySetAction.copy, + EntrySetAction.move, + EntrySetAction.refreshMetadata, + ].map((v) => _toMenuItem(v, enabled: hasSelection)), + ...[ + EntrySetAction.map, + EntrySetAction.stats, + ].map((v) => _toMenuItem(v, enabled: otherViewEnabled)), + if (!isSelecting && canAddShortcuts) ...[ + const PopupMenuDivider(), + _toMenuItem(EntrySetAction.addShortcut), + ], ], - ], - if (isSelecting) ...[ - const PopupMenuDivider(), - _toMenuItem( - CollectionAction.selectAll, - enabled: selectedItems.length < collection.entryCount, - ), - _toMenuItem( - CollectionAction.selectNone, - enabled: hasSelection, - ), - ] - ]; - }, - onSelected: (action) { - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onCollectionActionSelected(action)); - }, + if (isSelecting) ...[ + const PopupMenuDivider(), + _toMenuItem( + EntrySetAction.selectAll, + enabled: selectedItems.length < collection.entryCount, + ), + _toMenuItem( + EntrySetAction.selectNone, + enabled: hasSelection, + ), + ] + ]; + }, + onSelected: (action) { + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onCollectionActionSelected(action)); + }, + ), ); }, ), ]; } - PopupMenuItem _toMenuItem(CollectionAction action, {Key? key, bool enabled = true}) { + PopupMenuItem _toMenuItem(EntrySetAction action, {Key? key, bool enabled = true}) { return PopupMenuItem( key: key, value: action, enabled: enabled, - child: MenuRow( - text: action.getText(context), - icon: action.getIcon(), - ), + child: MenuRow(text: action.getText(context), icon: action.getIcon()), ); } @@ -285,28 +284,28 @@ class _CollectionAppBarState extends State with SingleTickerPr } } - Future _onCollectionActionSelected(CollectionAction action) async { + Future _onCollectionActionSelected(EntrySetAction action) async { switch (action) { - case CollectionAction.copy: - case CollectionAction.move: - case CollectionAction.refreshMetadata: - case CollectionAction.map: - case CollectionAction.stats: + case EntrySetAction.copy: + case EntrySetAction.move: + case EntrySetAction.refreshMetadata: + case EntrySetAction.map: + case EntrySetAction.stats: _actionDelegate.onCollectionActionSelected(context, action); break; - case CollectionAction.select: + case EntrySetAction.select: context.read>().select(); break; - case CollectionAction.selectAll: + case EntrySetAction.selectAll: context.read>().addToSelection(collection.sortedEntries); break; - case CollectionAction.selectNone: + case EntrySetAction.selectNone: context.read>().clearSelection(); break; - case CollectionAction.addShortcut: + case EntrySetAction.addShortcut: unawaited(_showShortcutDialog(context)); break; - case CollectionAction.group: + case EntrySetAction.group: final value = await showDialog( context: context, builder: (context) => AvesSelectionDialog( @@ -326,7 +325,7 @@ class _CollectionAppBarState extends State with SingleTickerPr settings.collectionSectionFactor = value; } break; - case CollectionAction.sort: + case EntrySetAction.sort: final value = await showDialog( context: context, builder: (context) => AvesSelectionDialog( diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index 0ea0d8456..4a09f2684 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:aves/model/actions/collection_actions.dart'; import 'package:aves/model/actions/entry_actions.dart'; +import 'package:aves/model/actions/entry_set_actions.dart'; import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/entry.dart'; import 'package:aves/model/filters/album.dart'; @@ -43,21 +43,21 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware } } - void onCollectionActionSelected(BuildContext context, CollectionAction action) { + void onCollectionActionSelected(BuildContext context, EntrySetAction action) { switch (action) { - case CollectionAction.copy: + case EntrySetAction.copy: _moveSelection(context, moveType: MoveType.copy); break; - case CollectionAction.move: + case EntrySetAction.move: _moveSelection(context, moveType: MoveType.move); break; - case CollectionAction.refreshMetadata: + case EntrySetAction.refreshMetadata: _refreshMetadata(context); break; - case CollectionAction.map: + case EntrySetAction.map: _goToMap(context); break; - case CollectionAction.stats: + case EntrySetAction.stats: _goToStats(context); break; default: diff --git a/lib/widgets/common/basic/menu.dart b/lib/widgets/common/basic/menu.dart new file mode 100644 index 000000000..3fe9f5583 --- /dev/null +++ b/lib/widgets/common/basic/menu.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class MenuRow extends StatelessWidget { + final String text; + final Widget? icon; + + const MenuRow({ + Key? key, + required this.text, + this.icon, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 8), + child: icon, + ), + Expanded(child: Text(text)), + ], + ); + } +} + +// scale icons according to text scale +class MenuIconTheme extends StatelessWidget { + final Widget child; + + const MenuIconTheme({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final iconTheme = IconTheme.of(context); + return IconTheme( + data: iconTheme.copyWith( + size: iconTheme.size! * MediaQuery.textScaleFactorOf(context), + ), + child: child, + ); + } +} diff --git a/lib/widgets/common/basic/menu_row.dart b/lib/widgets/common/basic/menu_row.dart deleted file mode 100644 index 81492121a..000000000 --- a/lib/widgets/common/basic/menu_row.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:aves/theme/icons.dart'; -import 'package:flutter/material.dart'; - -class MenuRow extends StatelessWidget { - final String text; - final IconData? icon; - final bool? checked; - - const MenuRow({ - Key? key, - required this.text, - this.icon, - this.checked, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = IconTheme.of(context).size! * textScaleFactor; - return Row( - children: [ - if (checked != null) ...[ - Opacity( - opacity: checked! ? 1 : 0, - child: Icon(AIcons.checked, size: iconSize), - ), - const SizedBox(width: 8), - ], - if (icon != null) ...[ - Icon(icon, size: iconSize), - const SizedBox(width: 8), - ], - Expanded(child: Text(text)), - ], - ); - } -} diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index d43fa431a..37ec5aa5d 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -7,7 +7,7 @@ import 'package:aves/model/filters/tag.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/constants.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -74,7 +74,9 @@ class AvesFilterChip extends StatefulWidget { items: actions .map((action) => PopupMenuItem( value: action, - child: MenuRow(text: action.getText(context), icon: action.getIcon()), + child: MenuIconTheme( + child: MenuRow(text: action.getText(context), icon: action.getIcon()), + ), )) .toList(), ); diff --git a/lib/widgets/drawer/tile.dart b/lib/widgets/drawer/tile.dart index 7ceefb16f..575aa6483 100644 --- a/lib/widgets/drawer/tile.dart +++ b/lib/widgets/drawer/tile.dart @@ -3,6 +3,7 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/type.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/debug/app_debug_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; @@ -73,7 +74,10 @@ class DrawerPageIcon extends StatelessWidget { case TagListPage.routeName: return const Icon(AIcons.tag); case AppDebugPage.routeName: - return const Icon(AIcons.debug); + return ShaderMask( + shaderCallback: Themes.debugGradient.createShader, + child: const Icon(AIcons.debug), + ); default: return const SizedBox(); } diff --git a/lib/widgets/filter_grids/album_pick.dart b/lib/widgets/filter_grids/album_pick.dart index b691a2a0c..0c8cbd395 100644 --- a/lib/widgets/filter_grids/album_pick.dart +++ b/lib/widgets/filter_grids/album_pick.dart @@ -9,7 +9,7 @@ import 'package:aves/model/source/enums.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/basic/query_bar.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/empty.dart'; @@ -145,27 +145,29 @@ class AlbumPickAppBar extends StatelessWidget { }, tooltip: context.l10n.createAlbumTooltip, ), - PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - value: ChipSetAction.sort, - child: MenuRow(text: context.l10n.menuActionSort, icon: AIcons.sort), - ), - PopupMenuItem( - value: ChipSetAction.group, - child: MenuRow(text: context.l10n.menuActionGroup, icon: AIcons.group), - ), - ]; - }, - onSelected: (action) { - // remove focus, if any, to prevent the keyboard from showing up - // after the user is done with the popup menu - FocusManager.instance.primaryFocus?.unfocus(); + MenuIconTheme( + child: PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + value: ChipSetAction.sort, + child: MenuRow(text: context.l10n.menuActionSort, icon: const Icon(AIcons.sort)), + ), + PopupMenuItem( + value: ChipSetAction.group, + child: MenuRow(text: context.l10n.menuActionGroup, icon: const Icon(AIcons.group)), + ), + ]; + }, + onSelected: (action) { + // remove focus, if any, to prevent the keyboard from showing up + // after the user is done with the popup menu + FocusManager.instance.primaryFocus?.unfocus(); - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, {}, action)); - }, + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, {}, action)); + }, + ), ), ], floating: true, diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index c74fcf6a1..ffef65bd4 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -40,6 +40,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate { @override bool isValid(Set filters, ChipSetAction action) { switch (action) { + case ChipSetAction.createAlbum: case ChipSetAction.delete: case ChipSetAction.rename: return true; diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index 97ed5389b..6612f056d 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -32,6 +32,7 @@ abstract class ChipSetActionDelegate with FeedbackMi bool isValid(Set filters, ChipSetAction action) { final hasSelection = filters.isNotEmpty; switch (action) { + case ChipSetAction.createAlbum: case ChipSetAction.delete: case ChipSetAction.rename: return false; diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index 33731f99a..8c48c9433 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -8,7 +8,7 @@ import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar_title.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; import 'package:aves/widgets/search/search_button.dart'; @@ -133,10 +133,7 @@ class _FilterGridAppBarState extends State extends State applyAction(action) : null, tooltip: action.getText(context), ); @@ -171,66 +168,68 @@ class _FilterGridAppBarState extends State( - key: const Key('appbar-menu-button'), - itemBuilder: (context) { - final selectedItems = selection.selectedItems; - final hasSelection = selectedItems.isNotEmpty; - final hasItems = !widget.isEmpty; - final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection); + MenuIconTheme( + child: PopupMenuButton( + key: const Key('appbar-menu-button'), + itemBuilder: (context) { + final selectedItems = selection.selectedItems; + final hasSelection = selectedItems.isNotEmpty; + final hasItems = !widget.isEmpty; + final otherViewEnabled = (!isSelecting && hasItems) || (isSelecting && hasSelection); - final menuItems = >[ - toMenuItem(ChipSetAction.sort), - if (widget.groupable) toMenuItem(ChipSetAction.group), - if (appMode == AppMode.main && !isSelecting) - toMenuItem( - ChipSetAction.select, - enabled: hasItems, - ), - ]; + final menuItems = >[ + toMenuItem(ChipSetAction.sort), + if (widget.groupable) toMenuItem(ChipSetAction.group), + if (appMode == AppMode.main && !isSelecting) + toMenuItem( + ChipSetAction.select, + enabled: hasItems, + ), + ]; - if (appMode == AppMode.main) { - menuItems.add(const PopupMenuDivider()); - if (isSelecting) { - menuItems.addAll(selectionRowActions.map(toMenuItem)); + if (appMode == AppMode.main) { + menuItems.add(const PopupMenuDivider()); + if (isSelecting) { + menuItems.addAll(selectionRowActions.map(toMenuItem)); + } + menuItems.addAll([ + toMenuItem( + ChipSetAction.map, + enabled: otherViewEnabled, + ), + toMenuItem( + ChipSetAction.stats, + enabled: otherViewEnabled, + ), + ]); + if (!isSelecting && actionDelegate.isValid(selectedFilters, ChipSetAction.createAlbum)) { + menuItems.addAll([ + const PopupMenuDivider(), + toMenuItem(ChipSetAction.createAlbum), + ]); + } } - menuItems.addAll([ - toMenuItem( - ChipSetAction.map, - enabled: otherViewEnabled, - ), - toMenuItem( - ChipSetAction.stats, - enabled: otherViewEnabled, - ), - ]); - if (!isSelecting) { + if (isSelecting) { menuItems.addAll([ const PopupMenuDivider(), - toMenuItem(ChipSetAction.createAlbum), + toMenuItem( + ChipSetAction.selectAll, + enabled: selectedItems.length < actionDelegate.allItems.length, + ), + toMenuItem( + ChipSetAction.selectNone, + enabled: hasSelection, + ), ]); } - } - if (isSelecting) { - menuItems.addAll([ - const PopupMenuDivider(), - toMenuItem( - ChipSetAction.selectAll, - enabled: selectedItems.length < actionDelegate.allItems.length, - ), - toMenuItem( - ChipSetAction.selectNone, - enabled: hasSelection, - ), - ]); - } - return menuItems; - }, - onSelected: (action) { - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => applyAction(action)); - }, + return menuItems; + }, + onSelected: (action) { + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => applyAction(action)); + }, + ), ), ]; } diff --git a/lib/widgets/settings/common/quick_actions/action_button.dart b/lib/widgets/settings/common/quick_actions/action_button.dart index 9e8c7f647..a941a1951 100644 --- a/lib/widgets/settings/common/quick_actions/action_button.dart +++ b/lib/widgets/settings/common/quick_actions/action_button.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class ActionButton extends StatelessWidget { final String text; - final IconData? icon; + final Widget? icon; final bool enabled, showCaption; const ActionButton({ @@ -27,7 +27,7 @@ class ActionButton extends StatelessWidget { const SizedBox(height: padding), OverlayButton( child: IconButton( - icon: Icon(icon), + icon: icon ?? const SizedBox(), onPressed: enabled ? () {} : null, ), ), diff --git a/lib/widgets/settings/common/quick_actions/available_actions.dart b/lib/widgets/settings/common/quick_actions/available_actions.dart index be1044939..2f1a9ed7d 100644 --- a/lib/widgets/settings/common/quick_actions/available_actions.dart +++ b/lib/widgets/settings/common/quick_actions/available_actions.dart @@ -10,7 +10,7 @@ class AvailableActionPanel extends StatelessWidget { final ValueNotifier draggedQuickAction; final ValueNotifier draggedAvailableAction; final bool Function(T? action) removeQuickAction; - final IconData? Function(T action) actionIcon; + final Widget? Function(T action) actionIcon; final String Function(BuildContext context, T action) actionText; const AvailableActionPanel({ diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 37058f902..eb3662b3c 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -18,7 +18,7 @@ import 'package:flutter/material.dart'; class QuickActionEditorPage extends StatefulWidget { final String title, bannerText; final List allAvailableActions; - final IconData? Function(T action) actionIcon; + final Widget? Function(T action) actionIcon; final String Function(BuildContext context, T action) actionText; final List Function() load; final void Function(List actions) save; @@ -287,7 +287,7 @@ class _QuickActionEditorPageState extends State with FeedbackMixin { appBar: AppBar( title: Text(context.l10n.settingsPageTitle), actions: [ - PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - value: SettingsAction.export, - child: MenuRow(text: context.l10n.settingsActionExport, icon: AIcons.export), - ), - PopupMenuItem( - value: SettingsAction.import, - child: MenuRow(text: context.l10n.settingsActionImport, icon: AIcons.import), - ), - ]; - }, - onSelected: (action) { - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(action)); - }, + MenuIconTheme( + child: PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + value: SettingsAction.export, + child: MenuRow(text: context.l10n.settingsActionExport, icon: const Icon(AIcons.export)), + ), + PopupMenuItem( + value: SettingsAction.import, + child: MenuRow(text: context.l10n.settingsActionImport, icon: const Icon(AIcons.import)), + ), + ]; + }, + onSelected: (action) { + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(action)); + }, + ), ), ], ), diff --git a/lib/widgets/viewer/overlay/bottom/video.dart b/lib/widgets/viewer/overlay/bottom/video.dart index 89aceb7f3..8014e9cff 100644 --- a/lib/widgets/viewer/overlay/bottom/video.dart +++ b/lib/widgets/viewer/overlay/bottom/video.dart @@ -8,7 +8,7 @@ import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/utils/time_utils.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/fx/blurred.dart'; import 'package:aves/widgets/common/fx/borders.dart'; @@ -224,12 +224,14 @@ class _ButtonRow extends StatelessWidget { padding: const EdgeInsetsDirectional.only(start: padding), child: OverlayButton( scale: scale, - child: PopupMenuButton( - itemBuilder: (context) => menuActions.map((action) => _buildPopupMenuItem(context, action)).toList(), - onSelected: (action) { - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => onActionSelected(action)); - }, + child: MenuIconTheme( + child: PopupMenuButton( + itemBuilder: (context) => menuActions.map((action) => _buildPopupMenuItem(context, action)).toList(), + onSelected: (action) { + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => onActionSelected(action)); + }, + ), ), ), ), @@ -249,7 +251,7 @@ class _ButtonRow extends StatelessWidget { onPressed: canDo ? onPressed : null, tooltip: action.getText(context), ), - child: Icon(action.getIcon()), + child: action.getIcon(), ); } @@ -273,7 +275,7 @@ class _ButtonRow extends StatelessWidget { case VideoAction.skip10: case VideoAction.settings: child = IconButton( - icon: Icon(action.getIcon()), + icon: action.getIcon(), onPressed: onPressed, tooltip: action.getText(context), ); @@ -401,11 +403,11 @@ class _PlayTogglerState extends State<_PlayToggler> with SingleTickerProviderSta return isPlaying ? MenuRow( text: context.l10n.videoActionPause, - icon: AIcons.pause, + icon: const Icon(AIcons.pause), ) : MenuRow( text: context.l10n.videoActionPlay, - icon: AIcons.play, + icon: const Icon(AIcons.play), ); } return IconButton( diff --git a/lib/widgets/viewer/overlay/top.dart b/lib/widgets/viewer/overlay/top.dart index ba720cc13..8244c8d2c 100644 --- a/lib/widgets/viewer/overlay/top.dart +++ b/lib/widgets/viewer/overlay/top.dart @@ -4,7 +4,7 @@ import 'package:aves/model/favourites.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/widgets/common/basic/menu_row.dart'; +import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/fx/sweeper.dart'; import 'package:aves/widgets/viewer/entry_action_delegate.dart'; @@ -166,22 +166,24 @@ class _TopOverlayRow extends StatelessWidget { ...quickActions.map((action) => _buildOverlayButton(context, action)), OverlayButton( scale: scale, - child: PopupMenuButton( - key: const Key('entry-menu-button'), - itemBuilder: (context) => [ - ...inAppActions.map((action) => _buildPopupMenuItem(context, action)), - if (pageEntry.canRotateAndFlip) _buildRotateAndFlipMenuItems(context), - const PopupMenuDivider(), - ...externalAppActions.map((action) => _buildPopupMenuItem(context, action)), - if (!kReleaseMode) ...[ + child: MenuIconTheme( + child: PopupMenuButton( + key: const Key('entry-menu-button'), + itemBuilder: (context) => [ + ...inAppActions.map((action) => _buildPopupMenuItem(context, action)), + if (pageEntry.canRotateAndFlip) _buildRotateAndFlipMenuItems(context), const PopupMenuDivider(), - _buildPopupMenuItem(context, EntryAction.debug), - ] - ], - onSelected: (action) { - // wait for the popup menu to hide before proceeding with the action - Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action)); - }, + ...externalAppActions.map((action) => _buildPopupMenuItem(context, action)), + if (!kReleaseMode) ...[ + const PopupMenuDivider(), + _buildPopupMenuItem(context, EntryAction.debug), + ] + ], + onSelected: (action) { + // wait for the popup menu to hide before proceeding with the action + Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action)); + }, + ), ), ), ], @@ -212,7 +214,7 @@ class _TopOverlayRow extends StatelessWidget { case EntryAction.viewSource: case EntryAction.viewMotionPhotoVideo: child = IconButton( - icon: Icon(action.getIcon()), + icon: action.getIcon() ?? const SizedBox(), onPressed: onPressed, tooltip: action.getText(context), ); @@ -269,7 +271,7 @@ class _TopOverlayRow extends StatelessWidget { value: action, child: Tooltip( message: action.getText(context), - child: Center(child: Icon(action.getIcon())), + child: Center(child: action.getIcon()), ), ), ); @@ -351,11 +353,11 @@ class _FavouriteTogglerState extends State<_FavouriteToggler> { return isFavourite ? MenuRow( text: context.l10n.entryActionRemoveFavourite, - icon: AIcons.favouriteActive, + icon: const Icon(AIcons.favouriteActive), ) : MenuRow( text: context.l10n.entryActionAddFavourite, - icon: AIcons.favourite, + icon: const Icon(AIcons.favourite), ); } return Stack(