menu fixes

This commit is contained in:
Thibault Deckers 2021-08-18 13:24:43 +09:00
parent 3aea36f3a7
commit c0af01578a
22 changed files with 348 additions and 282 deletions

View file

@ -23,7 +23,11 @@ extension ExtraChipAction on ChipAction {
} }
} }
IconData getIcon() { Widget getIcon() {
return Icon(_getIconData());
}
IconData _getIconData() {
switch (this) { switch (this) {
case ChipAction.goToAlbumPage: case ChipAction.goToAlbumPage:
return AIcons.album; return AIcons.album;

View file

@ -60,7 +60,11 @@ extension ExtraChipSetAction on ChipSetAction {
} }
} }
IconData? getIcon() { Widget getIcon() {
return Icon(_getIconData());
}
IconData _getIconData() {
switch (this) { switch (this) {
// general // general
case ChipSetAction.sort: case ChipSetAction.sort:

View file

@ -1,4 +1,5 @@
import 'package:aves/theme/icons.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/common/extensions/build_context.dart';
import 'package:flutter/widgets.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) { switch (this) {
case EntryAction.toggleFavourite: case EntryAction.toggleFavourite:
// different data depending on toggle state // different data depending on toggle state

View file

@ -2,7 +2,7 @@ import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
enum CollectionAction { enum EntrySetAction {
// general // general
sort, sort,
group, group,
@ -20,65 +20,69 @@ enum CollectionAction {
refreshMetadata, refreshMetadata,
} }
extension ExtraCollectionAction on CollectionAction { extension ExtraEntrySetAction on EntrySetAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
// general // general
case CollectionAction.sort: case EntrySetAction.sort:
return context.l10n.menuActionSort; return context.l10n.menuActionSort;
case CollectionAction.group: case EntrySetAction.group:
return context.l10n.menuActionGroup; return context.l10n.menuActionGroup;
case CollectionAction.select: case EntrySetAction.select:
return context.l10n.menuActionSelect; return context.l10n.menuActionSelect;
case CollectionAction.selectAll: case EntrySetAction.selectAll:
return context.l10n.menuActionSelectAll; return context.l10n.menuActionSelectAll;
case CollectionAction.selectNone: case EntrySetAction.selectNone:
return context.l10n.menuActionSelectNone; return context.l10n.menuActionSelectNone;
// all // all
case CollectionAction.addShortcut: case EntrySetAction.addShortcut:
return context.l10n.collectionActionAddShortcut; return context.l10n.collectionActionAddShortcut;
// all or entry selection // all or entry selection
case CollectionAction.map: case EntrySetAction.map:
return context.l10n.menuActionMap; return context.l10n.menuActionMap;
case CollectionAction.stats: case EntrySetAction.stats:
return context.l10n.menuActionStats; return context.l10n.menuActionStats;
// entry selection // entry selection
case CollectionAction.copy: case EntrySetAction.copy:
return context.l10n.collectionActionCopy; return context.l10n.collectionActionCopy;
case CollectionAction.move: case EntrySetAction.move:
return context.l10n.collectionActionMove; return context.l10n.collectionActionMove;
case CollectionAction.refreshMetadata: case EntrySetAction.refreshMetadata:
return context.l10n.collectionActionRefreshMetadata; return context.l10n.collectionActionRefreshMetadata;
} }
} }
IconData? getIcon() { Widget getIcon() {
return Icon(_getIconData());
}
IconData _getIconData() {
switch (this) { switch (this) {
// general // general
case CollectionAction.sort: case EntrySetAction.sort:
return AIcons.sort; return AIcons.sort;
case CollectionAction.group: case EntrySetAction.group:
return AIcons.group; return AIcons.group;
case CollectionAction.select: case EntrySetAction.select:
return AIcons.select; return AIcons.select;
case CollectionAction.selectAll: case EntrySetAction.selectAll:
return AIcons.selected; return AIcons.selected;
case CollectionAction.selectNone: case EntrySetAction.selectNone:
return AIcons.unselected; return AIcons.unselected;
// all // all
case CollectionAction.addShortcut: case EntrySetAction.addShortcut:
return AIcons.addShortcut; return AIcons.addShortcut;
// all or entry selection // all or entry selection
case CollectionAction.map: case EntrySetAction.map:
return AIcons.map; return AIcons.map;
case CollectionAction.stats: case EntrySetAction.stats:
return AIcons.stats; return AIcons.stats;
// entry selection // entry selection
case CollectionAction.copy: case EntrySetAction.copy:
return AIcons.copy; return AIcons.copy;
case CollectionAction.move: case EntrySetAction.move:
return AIcons.move; return AIcons.move;
case CollectionAction.refreshMetadata: case EntrySetAction.refreshMetadata:
return AIcons.refresh; return AIcons.refresh;
} }
} }

View file

@ -46,7 +46,11 @@ extension ExtraVideoAction on VideoAction {
} }
} }
IconData? getIcon() { Widget getIcon() {
return Icon(_getIconData());
}
IconData _getIconData() {
switch (this) { switch (this) {
case VideoAction.captureFrame: case VideoAction.captureFrame:
return AIcons.captureFrame; return AIcons.captureFrame;

View file

@ -6,6 +6,15 @@ import 'package:flutter/services.dart';
class Themes { class Themes {
static const _accentColor = Colors.indigoAccent; static const _accentColor = Colors.indigoAccent;
static const debugGradient = LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.red,
Colors.amber,
],
);
static final darkTheme = ThemeData( static final darkTheme = ThemeData(
brightness: Brightness.dark, brightness: Brightness.dark,
accentColor: _accentColor, accentColor: _accentColor,

View file

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/app_mode.dart'; 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_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/selection.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/collection/filter_bar.dart';
import 'package:aves/widgets/common/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar_title.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/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart'; import 'package:aves/widgets/dialogs/add_shortcut_dialog.dart';
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart'; import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
@ -177,7 +177,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
...EntryActions.selection.map((action) => Selector<Selection<AvesEntry>, bool>( ...EntryActions.selection.map((action) => Selector<Selection<AvesEntry>, bool>(
selector: (context, selection) => selection.selectedItems.isEmpty, selector: (context, selection) => selection.selectedItems.isEmpty,
builder: (context, isEmpty, child) => IconButton( builder: (context, isEmpty, child) => IconButton(
icon: Icon(action.getIcon()), icon: action.getIcon() ?? const SizedBox(),
onPressed: isEmpty ? null : () => _actionDelegate.onEntryActionSelected(context, action), onPressed: isEmpty ? null : () => _actionDelegate.onEntryActionSelected(context, action),
tooltip: action.getText(context), tooltip: action.getText(context),
), ),
@ -186,7 +186,8 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
future: _canAddShortcutsLoader, future: _canAddShortcutsLoader,
builder: (context, snapshot) { builder: (context, snapshot) {
final canAddShortcuts = snapshot.data ?? false; final canAddShortcuts = snapshot.data ?? false;
return PopupMenuButton<CollectionAction>( return MenuIconTheme(
child: PopupMenuButton<EntrySetAction>(
key: const Key('appbar-menu-button'), key: const Key('appbar-menu-button'),
itemBuilder: (context) { itemBuilder: (context) {
final groupable = collection.sortFactor == EntrySortFactor.date; final groupable = collection.sortFactor == EntrySortFactor.date;
@ -199,44 +200,44 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
return [ return [
_toMenuItem( _toMenuItem(
CollectionAction.sort, EntrySetAction.sort,
key: const Key('menu-sort'), key: const Key('menu-sort'),
), ),
if (groupable) if (groupable)
_toMenuItem( _toMenuItem(
CollectionAction.group, EntrySetAction.group,
key: const Key('menu-group'), key: const Key('menu-group'),
), ),
if (appMode == AppMode.main) ...[ if (appMode == AppMode.main) ...[
if (!isSelecting) if (!isSelecting)
_toMenuItem( _toMenuItem(
CollectionAction.select, EntrySetAction.select,
enabled: hasItems, enabled: hasItems,
), ),
const PopupMenuDivider(), const PopupMenuDivider(),
if (isSelecting) if (isSelecting)
...[ ...[
CollectionAction.copy, EntrySetAction.copy,
CollectionAction.move, EntrySetAction.move,
CollectionAction.refreshMetadata, EntrySetAction.refreshMetadata,
].map((v) => _toMenuItem(v, enabled: hasSelection)), ].map((v) => _toMenuItem(v, enabled: hasSelection)),
...[ ...[
CollectionAction.map, EntrySetAction.map,
CollectionAction.stats, EntrySetAction.stats,
].map((v) => _toMenuItem(v, enabled: otherViewEnabled)), ].map((v) => _toMenuItem(v, enabled: otherViewEnabled)),
if (!isSelecting && canAddShortcuts) ...[ if (!isSelecting && canAddShortcuts) ...[
const PopupMenuDivider(), const PopupMenuDivider(),
_toMenuItem(CollectionAction.addShortcut), _toMenuItem(EntrySetAction.addShortcut),
], ],
], ],
if (isSelecting) ...[ if (isSelecting) ...[
const PopupMenuDivider(), const PopupMenuDivider(),
_toMenuItem( _toMenuItem(
CollectionAction.selectAll, EntrySetAction.selectAll,
enabled: selectedItems.length < collection.entryCount, enabled: selectedItems.length < collection.entryCount,
), ),
_toMenuItem( _toMenuItem(
CollectionAction.selectNone, EntrySetAction.selectNone,
enabled: hasSelection, enabled: hasSelection,
), ),
] ]
@ -246,21 +247,19 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
// wait for the popup menu to hide before proceeding with the action // wait for the popup menu to hide before proceeding with the action
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onCollectionActionSelected(action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onCollectionActionSelected(action));
}, },
),
); );
}, },
), ),
]; ];
} }
PopupMenuItem<CollectionAction> _toMenuItem(CollectionAction action, {Key? key, bool enabled = true}) { PopupMenuItem<EntrySetAction> _toMenuItem(EntrySetAction action, {Key? key, bool enabled = true}) {
return PopupMenuItem( return PopupMenuItem(
key: key, key: key,
value: action, value: action,
enabled: enabled, enabled: enabled,
child: MenuRow( child: MenuRow(text: action.getText(context), icon: action.getIcon()),
text: action.getText(context),
icon: action.getIcon(),
),
); );
} }
@ -285,28 +284,28 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
} }
} }
Future<void> _onCollectionActionSelected(CollectionAction action) async { Future<void> _onCollectionActionSelected(EntrySetAction action) async {
switch (action) { switch (action) {
case CollectionAction.copy: case EntrySetAction.copy:
case CollectionAction.move: case EntrySetAction.move:
case CollectionAction.refreshMetadata: case EntrySetAction.refreshMetadata:
case CollectionAction.map: case EntrySetAction.map:
case CollectionAction.stats: case EntrySetAction.stats:
_actionDelegate.onCollectionActionSelected(context, action); _actionDelegate.onCollectionActionSelected(context, action);
break; break;
case CollectionAction.select: case EntrySetAction.select:
context.read<Selection<AvesEntry>>().select(); context.read<Selection<AvesEntry>>().select();
break; break;
case CollectionAction.selectAll: case EntrySetAction.selectAll:
context.read<Selection<AvesEntry>>().addToSelection(collection.sortedEntries); context.read<Selection<AvesEntry>>().addToSelection(collection.sortedEntries);
break; break;
case CollectionAction.selectNone: case EntrySetAction.selectNone:
context.read<Selection<AvesEntry>>().clearSelection(); context.read<Selection<AvesEntry>>().clearSelection();
break; break;
case CollectionAction.addShortcut: case EntrySetAction.addShortcut:
unawaited(_showShortcutDialog(context)); unawaited(_showShortcutDialog(context));
break; break;
case CollectionAction.group: case EntrySetAction.group:
final value = await showDialog<EntryGroupFactor>( final value = await showDialog<EntryGroupFactor>(
context: context, context: context,
builder: (context) => AvesSelectionDialog<EntryGroupFactor>( builder: (context) => AvesSelectionDialog<EntryGroupFactor>(
@ -326,7 +325,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
settings.collectionSectionFactor = value; settings.collectionSectionFactor = value;
} }
break; break;
case CollectionAction.sort: case EntrySetAction.sort:
final value = await showDialog<EntrySortFactor>( final value = await showDialog<EntrySortFactor>(
context: context, context: context,
builder: (context) => AvesSelectionDialog<EntrySortFactor>( builder: (context) => AvesSelectionDialog<EntrySortFactor>(

View file

@ -1,7 +1,7 @@
import 'dart:async'; 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_actions.dart';
import 'package:aves/model/actions/entry_set_actions.dart';
import 'package:aves/model/actions/move_type.dart'; import 'package:aves/model/actions/move_type.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/filters/album.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) { switch (action) {
case CollectionAction.copy: case EntrySetAction.copy:
_moveSelection(context, moveType: MoveType.copy); _moveSelection(context, moveType: MoveType.copy);
break; break;
case CollectionAction.move: case EntrySetAction.move:
_moveSelection(context, moveType: MoveType.move); _moveSelection(context, moveType: MoveType.move);
break; break;
case CollectionAction.refreshMetadata: case EntrySetAction.refreshMetadata:
_refreshMetadata(context); _refreshMetadata(context);
break; break;
case CollectionAction.map: case EntrySetAction.map:
_goToMap(context); _goToMap(context);
break; break;
case CollectionAction.stats: case EntrySetAction.stats:
_goToStats(context); _goToStats(context);
break; break;
default: default:

View file

@ -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,
);
}
}

View file

@ -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)),
],
);
}
}

View file

@ -7,7 +7,7 @@ import 'package:aves/model/filters/tag.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/constants.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:aves/widgets/filter_grids/common/action_delegates/chip.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
@ -74,7 +74,9 @@ class AvesFilterChip extends StatefulWidget {
items: actions items: actions
.map((action) => PopupMenuItem( .map((action) => PopupMenuItem(
value: action, value: action,
child: MenuIconTheme(
child: MenuRow(text: action.getText(context), icon: action.getIcon()), child: MenuRow(text: action.getText(context), icon: action.getIcon()),
),
)) ))
.toList(), .toList(),
); );

View file

@ -3,6 +3,7 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/filters/type.dart'; import 'package:aves/model/filters/type.dart';
import 'package:aves/theme/icons.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/common/extensions/build_context.dart';
import 'package:aves/widgets/debug/app_debug_page.dart'; import 'package:aves/widgets/debug/app_debug_page.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';
@ -73,7 +74,10 @@ class DrawerPageIcon extends StatelessWidget {
case TagListPage.routeName: case TagListPage.routeName:
return const Icon(AIcons.tag); return const Icon(AIcons.tag);
case AppDebugPage.routeName: case AppDebugPage.routeName:
return const Icon(AIcons.debug); return ShaderMask(
shaderCallback: Themes.debugGradient.createShader,
child: const Icon(AIcons.debug),
);
default: default:
return const SizedBox(); return const SizedBox();
} }

View file

@ -9,7 +9,7 @@ import 'package:aves/model/source/enums.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/app_bar_subtitle.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/basic/query_bar.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/identity/empty.dart';
@ -145,16 +145,17 @@ class AlbumPickAppBar extends StatelessWidget {
}, },
tooltip: context.l10n.createAlbumTooltip, tooltip: context.l10n.createAlbumTooltip,
), ),
PopupMenuButton<ChipSetAction>( MenuIconTheme(
child: PopupMenuButton<ChipSetAction>(
itemBuilder: (context) { itemBuilder: (context) {
return [ return [
PopupMenuItem( PopupMenuItem(
value: ChipSetAction.sort, value: ChipSetAction.sort,
child: MenuRow(text: context.l10n.menuActionSort, icon: AIcons.sort), child: MenuRow(text: context.l10n.menuActionSort, icon: const Icon(AIcons.sort)),
), ),
PopupMenuItem( PopupMenuItem(
value: ChipSetAction.group, value: ChipSetAction.group,
child: MenuRow(text: context.l10n.menuActionGroup, icon: AIcons.group), child: MenuRow(text: context.l10n.menuActionGroup, icon: const Icon(AIcons.group)),
), ),
]; ];
}, },
@ -167,6 +168,7 @@ class AlbumPickAppBar extends StatelessWidget {
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, {}, action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, {}, action));
}, },
), ),
),
], ],
floating: true, floating: true,
); );

View file

@ -40,6 +40,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
@override @override
bool isValid(Set<AlbumFilter> filters, ChipSetAction action) { bool isValid(Set<AlbumFilter> filters, ChipSetAction action) {
switch (action) { switch (action) {
case ChipSetAction.createAlbum:
case ChipSetAction.delete: case ChipSetAction.delete:
case ChipSetAction.rename: case ChipSetAction.rename:
return true; return true;

View file

@ -32,6 +32,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
bool isValid(Set<T> filters, ChipSetAction action) { bool isValid(Set<T> filters, ChipSetAction action) {
final hasSelection = filters.isNotEmpty; final hasSelection = filters.isNotEmpty;
switch (action) { switch (action) {
case ChipSetAction.createAlbum:
case ChipSetAction.delete: case ChipSetAction.delete:
case ChipSetAction.rename: case ChipSetAction.rename:
return false; return false;

View file

@ -8,7 +8,7 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/app_bar_subtitle.dart'; import 'package:aves/widgets/common/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar_title.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/common/extensions/build_context.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart';
import 'package:aves/widgets/search/search_button.dart'; import 'package:aves/widgets/search/search_button.dart';
@ -133,10 +133,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
return PopupMenuItem( return PopupMenuItem(
value: action, value: action,
enabled: enabled && actionDelegate.canApply(selectedFilters, action), enabled: enabled && actionDelegate.canApply(selectedFilters, action),
child: MenuRow( child: MenuRow(text: action.getText(context), icon: action.getIcon()),
text: action.getText(context),
icon: action.getIcon(),
),
); );
} }
@ -158,7 +155,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
(action) { (action) {
final enabled = actionDelegate.canApply(selectedFilters, action); final enabled = actionDelegate.canApply(selectedFilters, action);
return IconButton( return IconButton(
icon: Icon(action.getIcon()), icon: action.getIcon(),
onPressed: enabled ? () => applyAction(action) : null, onPressed: enabled ? () => applyAction(action) : null,
tooltip: action.getText(context), tooltip: action.getText(context),
); );
@ -171,7 +168,8 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
return [ return [
...buttonActions, ...buttonActions,
PopupMenuButton<ChipSetAction>( MenuIconTheme(
child: PopupMenuButton<ChipSetAction>(
key: const Key('appbar-menu-button'), key: const Key('appbar-menu-button'),
itemBuilder: (context) { itemBuilder: (context) {
final selectedItems = selection.selectedItems; final selectedItems = selection.selectedItems;
@ -204,7 +202,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
enabled: otherViewEnabled, enabled: otherViewEnabled,
), ),
]); ]);
if (!isSelecting) { if (!isSelecting && actionDelegate.isValid(selectedFilters, ChipSetAction.createAlbum)) {
menuItems.addAll([ menuItems.addAll([
const PopupMenuDivider(), const PopupMenuDivider(),
toMenuItem(ChipSetAction.createAlbum), toMenuItem(ChipSetAction.createAlbum),
@ -232,6 +230,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => applyAction(action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => applyAction(action));
}, },
), ),
),
]; ];
} }

View file

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget { class ActionButton extends StatelessWidget {
final String text; final String text;
final IconData? icon; final Widget? icon;
final bool enabled, showCaption; final bool enabled, showCaption;
const ActionButton({ const ActionButton({
@ -27,7 +27,7 @@ class ActionButton extends StatelessWidget {
const SizedBox(height: padding), const SizedBox(height: padding),
OverlayButton( OverlayButton(
child: IconButton( child: IconButton(
icon: Icon(icon), icon: icon ?? const SizedBox(),
onPressed: enabled ? () {} : null, onPressed: enabled ? () {} : null,
), ),
), ),

View file

@ -10,7 +10,7 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
final ValueNotifier<T?> draggedQuickAction; final ValueNotifier<T?> draggedQuickAction;
final ValueNotifier<T?> draggedAvailableAction; final ValueNotifier<T?> draggedAvailableAction;
final bool Function(T? action) removeQuickAction; 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; final String Function(BuildContext context, T action) actionText;
const AvailableActionPanel({ const AvailableActionPanel({

View file

@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
class QuickActionEditorPage<T extends Object> extends StatefulWidget { class QuickActionEditorPage<T extends Object> extends StatefulWidget {
final String title, bannerText; final String title, bannerText;
final List<T> allAvailableActions; final List<T> allAvailableActions;
final IconData? Function(T action) actionIcon; final Widget? Function(T action) actionIcon;
final String Function(BuildContext context, T action) actionText; final String Function(BuildContext context, T action) actionText;
final List<T> Function() load; final List<T> Function() load;
final void Function(List<T> actions) save; final void Function(List<T> actions) save;
@ -287,7 +287,7 @@ class _QuickActionEditorPageState<T extends Object> extends State<QuickActionEdi
padding: const EdgeInsets.symmetric(vertical: _QuickActionEditorPageState.quickActionVerticalPadding, horizontal: 4), padding: const EdgeInsets.symmetric(vertical: _QuickActionEditorPageState.quickActionVerticalPadding, horizontal: 4),
child: OverlayButton( child: OverlayButton(
child: IconButton( child: IconButton(
icon: Icon(widget.actionIcon(action)), icon: widget.actionIcon(action) ?? const SizedBox(),
onPressed: () {}, onPressed: () {},
), ),
), ),

View file

@ -8,7 +8,7 @@ import 'package:aves/services/services.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/action_mixins/feedback.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/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/settings/language/language.dart'; import 'package:aves/widgets/settings/language/language.dart';
@ -42,16 +42,17 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
appBar: AppBar( appBar: AppBar(
title: Text(context.l10n.settingsPageTitle), title: Text(context.l10n.settingsPageTitle),
actions: [ actions: [
PopupMenuButton<SettingsAction>( MenuIconTheme(
child: PopupMenuButton<SettingsAction>(
itemBuilder: (context) { itemBuilder: (context) {
return [ return [
PopupMenuItem( PopupMenuItem(
value: SettingsAction.export, value: SettingsAction.export,
child: MenuRow(text: context.l10n.settingsActionExport, icon: AIcons.export), child: MenuRow(text: context.l10n.settingsActionExport, icon: const Icon(AIcons.export)),
), ),
PopupMenuItem( PopupMenuItem(
value: SettingsAction.import, value: SettingsAction.import,
child: MenuRow(text: context.l10n.settingsActionImport, icon: AIcons.import), child: MenuRow(text: context.l10n.settingsActionImport, icon: const Icon(AIcons.import)),
), ),
]; ];
}, },
@ -60,6 +61,7 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(action));
}, },
), ),
),
], ],
), ),
body: Theme( body: Theme(

View file

@ -8,7 +8,7 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/utils/time_utils.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/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/blurred.dart'; import 'package:aves/widgets/common/fx/blurred.dart';
import 'package:aves/widgets/common/fx/borders.dart'; import 'package:aves/widgets/common/fx/borders.dart';
@ -224,6 +224,7 @@ class _ButtonRow extends StatelessWidget {
padding: const EdgeInsetsDirectional.only(start: padding), padding: const EdgeInsetsDirectional.only(start: padding),
child: OverlayButton( child: OverlayButton(
scale: scale, scale: scale,
child: MenuIconTheme(
child: PopupMenuButton<VideoAction>( child: PopupMenuButton<VideoAction>(
itemBuilder: (context) => menuActions.map((action) => _buildPopupMenuItem(context, action)).toList(), itemBuilder: (context) => menuActions.map((action) => _buildPopupMenuItem(context, action)).toList(),
onSelected: (action) { onSelected: (action) {
@ -233,6 +234,7 @@ class _ButtonRow extends StatelessWidget {
), ),
), ),
), ),
),
], ],
); );
} }
@ -249,7 +251,7 @@ class _ButtonRow extends StatelessWidget {
onPressed: canDo ? onPressed : null, onPressed: canDo ? onPressed : null,
tooltip: action.getText(context), tooltip: action.getText(context),
), ),
child: Icon(action.getIcon()), child: action.getIcon(),
); );
} }
@ -273,7 +275,7 @@ class _ButtonRow extends StatelessWidget {
case VideoAction.skip10: case VideoAction.skip10:
case VideoAction.settings: case VideoAction.settings:
child = IconButton( child = IconButton(
icon: Icon(action.getIcon()), icon: action.getIcon(),
onPressed: onPressed, onPressed: onPressed,
tooltip: action.getText(context), tooltip: action.getText(context),
); );
@ -401,11 +403,11 @@ class _PlayTogglerState extends State<_PlayToggler> with SingleTickerProviderSta
return isPlaying return isPlaying
? MenuRow( ? MenuRow(
text: context.l10n.videoActionPause, text: context.l10n.videoActionPause,
icon: AIcons.pause, icon: const Icon(AIcons.pause),
) )
: MenuRow( : MenuRow(
text: context.l10n.videoActionPlay, text: context.l10n.videoActionPlay,
icon: AIcons.play, icon: const Icon(AIcons.play),
); );
} }
return IconButton( return IconButton(

View file

@ -4,7 +4,7 @@ import 'package:aves/model/favourites.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.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/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/sweeper.dart'; import 'package:aves/widgets/common/fx/sweeper.dart';
import 'package:aves/widgets/viewer/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/entry_action_delegate.dart';
@ -166,6 +166,7 @@ class _TopOverlayRow extends StatelessWidget {
...quickActions.map((action) => _buildOverlayButton(context, action)), ...quickActions.map((action) => _buildOverlayButton(context, action)),
OverlayButton( OverlayButton(
scale: scale, scale: scale,
child: MenuIconTheme(
child: PopupMenuButton<EntryAction>( child: PopupMenuButton<EntryAction>(
key: const Key('entry-menu-button'), key: const Key('entry-menu-button'),
itemBuilder: (context) => [ itemBuilder: (context) => [
@ -184,6 +185,7 @@ class _TopOverlayRow extends StatelessWidget {
}, },
), ),
), ),
),
], ],
); );
} }
@ -212,7 +214,7 @@ class _TopOverlayRow extends StatelessWidget {
case EntryAction.viewSource: case EntryAction.viewSource:
case EntryAction.viewMotionPhotoVideo: case EntryAction.viewMotionPhotoVideo:
child = IconButton( child = IconButton(
icon: Icon(action.getIcon()), icon: action.getIcon() ?? const SizedBox(),
onPressed: onPressed, onPressed: onPressed,
tooltip: action.getText(context), tooltip: action.getText(context),
); );
@ -269,7 +271,7 @@ class _TopOverlayRow extends StatelessWidget {
value: action, value: action,
child: Tooltip( child: Tooltip(
message: action.getText(context), 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 return isFavourite
? MenuRow( ? MenuRow(
text: context.l10n.entryActionRemoveFavourite, text: context.l10n.entryActionRemoveFavourite,
icon: AIcons.favouriteActive, icon: const Icon(AIcons.favouriteActive),
) )
: MenuRow( : MenuRow(
text: context.l10n.entryActionAddFavourite, text: context.l10n.entryActionAddFavourite,
icon: AIcons.favourite, icon: const Icon(AIcons.favourite),
); );
} }
return Stack( return Stack(