menu fixes
This commit is contained in:
parent
3aea36f3a7
commit
c0af01578a
22 changed files with 348 additions and 282 deletions
|
@ -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;
|
||||
|
|
|
@ -60,7 +60,11 @@ extension ExtraChipSetAction on ChipSetAction {
|
|||
}
|
||||
}
|
||||
|
||||
IconData? getIcon() {
|
||||
Widget getIcon() {
|
||||
return Icon(_getIconData());
|
||||
}
|
||||
|
||||
IconData _getIconData() {
|
||||
switch (this) {
|
||||
// general
|
||||
case ChipSetAction.sort:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<CollectionAppBar> with SingleTickerPr
|
|||
...EntryActions.selection.map((action) => Selector<Selection<AvesEntry>, 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<CollectionAppBar> with SingleTickerPr
|
|||
future: _canAddShortcutsLoader,
|
||||
builder: (context, snapshot) {
|
||||
final canAddShortcuts = snapshot.data ?? false;
|
||||
return PopupMenuButton<CollectionAction>(
|
||||
key: const Key('appbar-menu-button'),
|
||||
itemBuilder: (context) {
|
||||
final groupable = collection.sortFactor == EntrySortFactor.date;
|
||||
final selection = context.read<Selection<AvesEntry>>();
|
||||
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<EntrySetAction>(
|
||||
key: const Key('appbar-menu-button'),
|
||||
itemBuilder: (context) {
|
||||
final groupable = collection.sortFactor == EntrySortFactor.date;
|
||||
final selection = context.read<Selection<AvesEntry>>();
|
||||
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<CollectionAction> _toMenuItem(CollectionAction action, {Key? key, bool enabled = true}) {
|
||||
PopupMenuItem<EntrySetAction> _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<CollectionAppBar> with SingleTickerPr
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _onCollectionActionSelected(CollectionAction action) async {
|
||||
Future<void> _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<Selection<AvesEntry>>().select();
|
||||
break;
|
||||
case CollectionAction.selectAll:
|
||||
case EntrySetAction.selectAll:
|
||||
context.read<Selection<AvesEntry>>().addToSelection(collection.sortedEntries);
|
||||
break;
|
||||
case CollectionAction.selectNone:
|
||||
case EntrySetAction.selectNone:
|
||||
context.read<Selection<AvesEntry>>().clearSelection();
|
||||
break;
|
||||
case CollectionAction.addShortcut:
|
||||
case EntrySetAction.addShortcut:
|
||||
unawaited(_showShortcutDialog(context));
|
||||
break;
|
||||
case CollectionAction.group:
|
||||
case EntrySetAction.group:
|
||||
final value = await showDialog<EntryGroupFactor>(
|
||||
context: context,
|
||||
builder: (context) => AvesSelectionDialog<EntryGroupFactor>(
|
||||
|
@ -326,7 +325,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
settings.collectionSectionFactor = value;
|
||||
}
|
||||
break;
|
||||
case CollectionAction.sort:
|
||||
case EntrySetAction.sort:
|
||||
final value = await showDialog<EntrySortFactor>(
|
||||
context: context,
|
||||
builder: (context) => AvesSelectionDialog<EntrySortFactor>(
|
||||
|
|
|
@ -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:
|
||||
|
|
47
lib/widgets/common/basic/menu.dart
Normal file
47
lib/widgets/common/basic/menu.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<ChipSetAction>(
|
||||
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<ChipSetAction>(
|
||||
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,
|
||||
|
|
|
@ -40,6 +40,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> {
|
|||
@override
|
||||
bool isValid(Set<AlbumFilter> filters, ChipSetAction action) {
|
||||
switch (action) {
|
||||
case ChipSetAction.createAlbum:
|
||||
case ChipSetAction.delete:
|
||||
case ChipSetAction.rename:
|
||||
return true;
|
||||
|
|
|
@ -32,6 +32,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
|||
bool isValid(Set<T> filters, ChipSetAction action) {
|
||||
final hasSelection = filters.isNotEmpty;
|
||||
switch (action) {
|
||||
case ChipSetAction.createAlbum:
|
||||
case ChipSetAction.delete:
|
||||
case ChipSetAction.rename:
|
||||
return false;
|
||||
|
|
|
@ -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<T extends CollectionFilter> extends State<FilterGri
|
|||
return PopupMenuItem(
|
||||
value: action,
|
||||
enabled: enabled && actionDelegate.canApply(selectedFilters, action),
|
||||
child: MenuRow(
|
||||
text: action.getText(context),
|
||||
icon: action.getIcon(),
|
||||
),
|
||||
child: MenuRow(text: action.getText(context), icon: action.getIcon()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -158,7 +155,7 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
|
|||
(action) {
|
||||
final enabled = actionDelegate.canApply(selectedFilters, action);
|
||||
return IconButton(
|
||||
icon: Icon(action.getIcon()),
|
||||
icon: action.getIcon(),
|
||||
onPressed: enabled ? () => applyAction(action) : null,
|
||||
tooltip: action.getText(context),
|
||||
);
|
||||
|
@ -171,66 +168,68 @@ class _FilterGridAppBarState<T extends CollectionFilter> extends State<FilterGri
|
|||
|
||||
return [
|
||||
...buttonActions,
|
||||
PopupMenuButton<ChipSetAction>(
|
||||
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<ChipSetAction>(
|
||||
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 = <PopupMenuEntry<ChipSetAction>>[
|
||||
toMenuItem(ChipSetAction.sort),
|
||||
if (widget.groupable) toMenuItem(ChipSetAction.group),
|
||||
if (appMode == AppMode.main && !isSelecting)
|
||||
toMenuItem(
|
||||
ChipSetAction.select,
|
||||
enabled: hasItems,
|
||||
),
|
||||
];
|
||||
final menuItems = <PopupMenuEntry<ChipSetAction>>[
|
||||
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));
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -10,7 +10,7 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
|
|||
final ValueNotifier<T?> draggedQuickAction;
|
||||
final ValueNotifier<T?> 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({
|
||||
|
|
|
@ -18,7 +18,7 @@ import 'package:flutter/material.dart';
|
|||
class QuickActionEditorPage<T extends Object> extends StatefulWidget {
|
||||
final String title, bannerText;
|
||||
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 List<T> Function() load;
|
||||
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),
|
||||
child: OverlayButton(
|
||||
child: IconButton(
|
||||
icon: Icon(widget.actionIcon(action)),
|
||||
icon: widget.actionIcon(action) ?? const SizedBox(),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:aves/services/services.dart';
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.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/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/settings/language/language.dart';
|
||||
|
@ -42,23 +42,25 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
|
|||
appBar: AppBar(
|
||||
title: Text(context.l10n.settingsPageTitle),
|
||||
actions: [
|
||||
PopupMenuButton<SettingsAction>(
|
||||
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<SettingsAction>(
|
||||
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));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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<VideoAction>(
|
||||
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<VideoAction>(
|
||||
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(
|
||||
|
|
|
@ -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<EntryAction>(
|
||||
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<EntryAction>(
|
||||
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(
|
||||
|
|
Loading…
Reference in a new issue