selection: delete

This commit is contained in:
Thibault Deckers 2020-04-24 13:05:03 +09:00
parent 1751b7b3d7
commit a08c5a3369
8 changed files with 319 additions and 210 deletions

View file

@ -31,7 +31,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
groupFactor = groupFactor ?? GroupFactor.month,
sortFactor = sortFactor ?? SortFactor.date {
_subscriptions.add(source.eventBus.on<EntryAddedEvent>().listen((e) => onEntryAdded()));
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entry)));
_subscriptions.add(source.eventBus.on<EntryRemovedEvent>().listen((e) => onEntryRemoved(e.entries)));
_subscriptions.add(source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => onMetadataChanged()));
onEntryAdded();
}
@ -66,6 +66,7 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
int get entryCount => _filteredEntries.length;
// sorted as displayed to the user, i.e. sorted then grouped, not an absolute order on all entries
List<ImageEntry> _sortedEntries;
List<ImageEntry> get sortedEntries {
@ -180,11 +181,12 @@ class CollectionLens with ChangeNotifier, CollectionActivityMixin, CollectionSel
_applyGroup();
}
void onEntryRemoved(ImageEntry entry) {
void onEntryRemoved(Iterable<ImageEntry> entries) {
// do not apply sort/group as section order change would surprise the user while browsing
_filteredEntries.remove(entry);
_sortedEntries?.remove(entry);
sections.forEach((key, entries) => entries.remove(entry));
_filteredEntries.removeWhere(entries.contains);
_sortedEntries?.removeWhere(entries.contains);
sections.forEach((key, sectionEntries) => sectionEntries.removeWhere(entries.contains));
selection.removeAll(entries);
notifyListeners();
}
@ -223,14 +225,14 @@ mixin CollectionSelectionMixin on CollectionActivityMixin {
Set<ImageEntry> get selection => _selection;
bool isSelected(List<ImageEntry> entries) => entries.every(selection.contains);
bool isSelected(Iterable<ImageEntry> entries) => entries.every(selection.contains);
void addToSelection(List<ImageEntry> entries) {
void addToSelection(Iterable<ImageEntry> entries) {
_selection.addAll(entries);
selectionChangeNotifier.notifyListeners();
}
void removeFromSelection(List<ImageEntry> entries) {
void removeFromSelection(Iterable<ImageEntry> entries) {
_selection.removeAll(entries);
selectionChangeNotifier.notifyListeners();
}

View file

@ -138,13 +138,9 @@ class CollectionSource {
eventBus.fire(const EntryAddedEvent());
}
Future<bool> delete(ImageEntry entry) async {
final success = await entry.delete();
if (success) {
_rawEntries.remove(entry);
eventBus.fire(EntryRemovedEvent(entry));
}
return success;
void removeEntries(Iterable<ImageEntry> entries) async {
_rawEntries.remove(entries);
eventBus.fire(EntryRemovedEvent(entries));
}
String getUniqueAlbumName(String album) {
@ -170,7 +166,7 @@ class EntryAddedEvent {
}
class EntryRemovedEvent {
final ImageEntry entry;
final Iterable<ImageEntry> entries;
const EntryRemovedEvent(this.entry);
const EntryRemovedEvent(this.entries);
}

View file

@ -1,19 +1,24 @@
import 'dart:async';
import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/model/settings.dart';
import 'package:aves/services/android_app_service.dart';
import 'package:aves/services/image_file_service.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/album/filter_bar.dart';
import 'package:aves/widgets/album/search/search_delegate.dart';
import 'package:aves/widgets/common/entry_actions.dart';
import 'package:aves/widgets/common/menu_row.dart';
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
import 'package:aves/widgets/stats/stats.dart';
import 'package:collection/collection.dart';
import 'package:flushbar/flushbar.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
import 'package:pedantic/pedantic.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
class CollectionAppBar extends StatefulWidget {
final ValueNotifier<double> appBarHeightNotifier;
@ -150,17 +155,16 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
onPressed: _goToSearch,
),
if (collection.isSelecting)
AnimatedBuilder(
...EntryActions.selection.map((action) => AnimatedBuilder(
animation: collection.selectionChangeNotifier,
builder: (context, child) {
const action = FullscreenAction.share;
return IconButton(
icon: Icon(action.getIcon()),
onPressed: collection.selection.isEmpty ? null : _shareSelection,
onPressed: collection.selection.isEmpty ? null : () => _onSelectionActionSelected(action),
tooltip: action.getText(),
);
},
),
)),
Builder(
builder: (context) => PopupMenuButton<CollectionAction>(
itemBuilder: (context) => [
@ -177,7 +181,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
),
],
],
onSelected: _onActionSelected,
onSelected: _onCollectionActionSelected,
),
),
];
@ -221,7 +225,20 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
: [];
}
void _onActionSelected(CollectionAction action) async {
void _onActivityChange() {
if (collection.isSelecting) {
_browseToSelectAnimation.forward();
} else {
_browseToSelectAnimation.reverse();
_searchFieldController.clear();
}
}
void _updateHeight() {
widget.appBarHeightNotifier.value = kToolbarHeight + (hasFilters ? FilterBar.preferredHeight : 0);
}
void _onCollectionActionSelected(CollectionAction action) async {
// wait for the popup menu to hide before proceeding with the action
await Future.delayed(Constants.popupMenuTransitionDuration);
switch (action) {
@ -277,22 +294,108 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
);
}
void _onSelectionActionSelected(EntryAction action) {
switch (action) {
case EntryAction.share:
_shareSelection();
break;
case EntryAction.delete:
_deleteSelection();
break;
default:
break;
}
}
void _shareSelection() {
final urisByMimeType = groupBy<ImageEntry, String>(collection.selection, (e) => e.mimeType).map((k, v) => MapEntry(k, v.map((e) => e.uri).toList()));
AndroidAppService.share(urisByMimeType);
}
void _onActivityChange() {
if (collection.isSelecting) {
_browseToSelectAnimation.forward();
} else {
_browseToSelectAnimation.reverse();
_searchFieldController.clear();
void _deleteSelection() {
final selection = collection.selection.toList();
_showOpReport(
selection: selection,
opStream: ImageFileService.delete(selection),
onDone: (processed) {
final deletedUris = processed.where((e) => e.success).map((e) => e.uri);
final deletedCount = deletedUris.length;
final selectionCount = selection.length;
if (deletedCount < selectionCount) {
_showFeedback(context, 'Failed to delete ${selectionCount - deletedCount} items');
}
if (deletedCount > 0) {
collection.source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)));
}
collection.browse();
},
);
}
void _updateHeight() {
widget.appBarHeightNotifier.value = kToolbarHeight + (hasFilters ? FilterBar.preferredHeight : 0);
// selection action report overlay
OverlayEntry _opReportOverlayEntry;
static const _overlayAnimationDuration = Duration(milliseconds: 300);
void _showOpReport({
@required List<ImageEntry> selection,
@required Stream<ImageOpEvent> opStream,
@required void Function(Set<ImageOpEvent> processed) onDone,
}) {
final processed = <ImageOpEvent>{};
_opReportOverlayEntry = OverlayEntry(
builder: (context) {
return StreamBuilder<ImageOpEvent>(
stream: opStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
processed.add(snapshot.data);
}
Widget child = const SizedBox.shrink();
if (snapshot.hasError || snapshot.connectionState == ConnectionState.done) {
_hideOpReportOverlay().then((_) => onDone(processed));
} else if (snapshot.connectionState == ConnectionState.active) {
final percent = processed.length.toDouble() / selection.length;
child = CircularPercentIndicator(
percent: percent,
lineWidth: 16,
radius: 160,
backgroundColor: Colors.white24,
progressColor: Theme.of(context).accentColor,
animation: true,
center: Text(NumberFormat.percentPattern().format(percent)),
animateFromLastPercent: true,
);
}
return AnimatedSwitcher(
duration: _overlayAnimationDuration,
child: child,
);
});
},
);
Overlay.of(context).insert(_opReportOverlayEntry);
}
Future<void> _hideOpReportOverlay() async {
await Future.delayed(_overlayAnimationDuration);
_opReportOverlayEntry.remove();
_opReportOverlayEntry = null;
}
void _showFeedback(BuildContext context, String message) {
Flushbar(
message: message,
margin: const EdgeInsets.all(8),
borderRadius: 8,
borderColor: Colors.white30,
borderWidth: 0.5,
duration: const Duration(seconds: 2),
flushbarPosition: FlushbarPosition.TOP,
animationDuration: const Duration(milliseconds: 600),
).show(context);
}
}

View file

@ -4,9 +4,9 @@ import 'package:aves/model/collection_lens.dart';
import 'package:aves/model/image_entry.dart';
import 'package:aves/services/android_app_service.dart';
import 'package:aves/services/image_file_service.dart';
import 'package:aves/widgets/common/entry_actions.dart';
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
import 'package:aves/widgets/fullscreen/debug.dart';
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
import 'package:flushbar/flushbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -15,58 +15,58 @@ import 'package:pdf/widgets.dart' as pdf;
import 'package:pedantic/pedantic.dart';
import 'package:printing/printing.dart';
class FullscreenActionDelegate {
class EntryActionDelegate {
final CollectionLens collection;
final VoidCallback showInfo;
FullscreenActionDelegate({
EntryActionDelegate({
@required this.collection,
@required this.showInfo,
});
bool get hasCollection => collection != null;
void onActionSelected(BuildContext context, ImageEntry entry, FullscreenAction action) {
void onActionSelected(BuildContext context, ImageEntry entry, EntryAction action) {
switch (action) {
case FullscreenAction.toggleFavourite:
case EntryAction.toggleFavourite:
entry.toggleFavourite();
break;
case FullscreenAction.delete:
case EntryAction.delete:
_showDeleteDialog(context, entry);
break;
case FullscreenAction.edit:
case EntryAction.edit:
AndroidAppService.edit(entry.uri, entry.mimeType);
break;
case FullscreenAction.info:
case EntryAction.info:
showInfo();
break;
case FullscreenAction.rename:
case EntryAction.rename:
_showRenameDialog(context, entry);
break;
case FullscreenAction.open:
case EntryAction.open:
AndroidAppService.open(entry.uri, entry.mimeTypeAnySubtype);
break;
case FullscreenAction.openMap:
case EntryAction.openMap:
AndroidAppService.openMap(entry.geoUri);
break;
case FullscreenAction.print:
case EntryAction.print:
_print(entry);
break;
case FullscreenAction.rotateCCW:
case EntryAction.rotateCCW:
_rotate(context, entry, clockwise: false);
break;
case FullscreenAction.rotateCW:
case EntryAction.rotateCW:
_rotate(context, entry, clockwise: true);
break;
case FullscreenAction.setAs:
case EntryAction.setAs:
AndroidAppService.setAs(entry.uri, entry.mimeType);
break;
case FullscreenAction.share:
case EntryAction.share:
AndroidAppService.share({
entry.mimeType: [entry.uri]
});
break;
case FullscreenAction.debug:
case EntryAction.debug:
_goToDebug(context, entry);
break;
}
@ -146,13 +146,16 @@ class FullscreenActionDelegate {
},
);
if (confirmed == null || !confirmed) return;
if (hasCollection) {
if (!await collection.source.delete(entry)) {
if (!await entry.delete()) {
_showFeedback(context, 'Failed');
} else if (collection.sortedEntries.isEmpty) {
} else if (hasCollection) {
// update collection
collection.source.removeEntries([entry]);
if (collection.sortedEntries.isEmpty) {
Navigator.pop(context);
}
} else if (await entry.delete()) {
} else {
// leave viewer
exit(0);
}
}

View file

@ -0,0 +1,99 @@
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
enum EntryAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite, debug }
class EntryActions {
static const selection = [
EntryAction.share,
EntryAction.delete,
];
static const inApp = [
EntryAction.info,
EntryAction.toggleFavourite,
EntryAction.share,
EntryAction.delete,
EntryAction.rename,
EntryAction.rotateCCW,
EntryAction.rotateCW,
EntryAction.print,
];
static const externalApp = [
EntryAction.edit,
EntryAction.open,
EntryAction.setAs,
EntryAction.openMap,
];
}
extension ExtraEntryAction on EntryAction {
String getText() {
switch (this) {
// in app actions
case EntryAction.toggleFavourite:
// different data depending on toggle state
return null;
case EntryAction.delete:
return 'Delete';
case EntryAction.info:
return 'Info';
case EntryAction.rename:
return 'Rename';
case EntryAction.rotateCCW:
return 'Rotate left';
case EntryAction.rotateCW:
return 'Rotate right';
case EntryAction.print:
return 'Print';
case EntryAction.share:
return 'Share';
// external app actions
case EntryAction.edit:
return 'Edit with…';
case EntryAction.open:
return 'Open with…';
case EntryAction.setAs:
return 'Set as…';
case EntryAction.openMap:
return 'Show on map…';
case EntryAction.debug:
return 'Debug';
}
return null;
}
IconData getIcon() {
switch (this) {
// in app actions
case EntryAction.toggleFavourite:
// different data depending on toggle state
return null;
case EntryAction.delete:
return AIcons.delete;
case EntryAction.info:
return OMIcons.info;
case EntryAction.rename:
return OMIcons.title;
case EntryAction.rotateCCW:
return AIcons.rotateLeft;
case EntryAction.rotateCW:
return AIcons.rotateRight;
case EntryAction.print:
return AIcons.print;
case EntryAction.share:
return AIcons.share;
// external app actions
case EntryAction.edit:
case EntryAction.open:
case EntryAction.setAs:
case EntryAction.openMap:
return null;
case EntryAction.debug:
return OMIcons.whatshot;
}
return null;
}
}

View file

@ -1,94 +0,0 @@
import 'package:aves/widgets/common/icons.dart';
import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite, debug }
class FullscreenActions {
static const inApp = [
FullscreenAction.info,
FullscreenAction.toggleFavourite,
FullscreenAction.share,
FullscreenAction.delete,
FullscreenAction.rename,
FullscreenAction.rotateCCW,
FullscreenAction.rotateCW,
FullscreenAction.print,
];
static const externalApp = [
FullscreenAction.edit,
FullscreenAction.open,
FullscreenAction.setAs,
FullscreenAction.openMap,
];
}
extension ExtraFullscreenAction on FullscreenAction {
String getText() {
switch (this) {
// in app actions
case FullscreenAction.toggleFavourite:
// different data depending on toggle state
return null;
case FullscreenAction.delete:
return 'Delete';
case FullscreenAction.info:
return 'Info';
case FullscreenAction.rename:
return 'Rename';
case FullscreenAction.rotateCCW:
return 'Rotate left';
case FullscreenAction.rotateCW:
return 'Rotate right';
case FullscreenAction.print:
return 'Print';
case FullscreenAction.share:
return 'Share';
// external app actions
case FullscreenAction.edit:
return 'Edit with…';
case FullscreenAction.open:
return 'Open with…';
case FullscreenAction.setAs:
return 'Set as…';
case FullscreenAction.openMap:
return 'Show on map…';
case FullscreenAction.debug:
return 'Debug';
}
return null;
}
IconData getIcon() {
switch (this) {
// in app actions
case FullscreenAction.toggleFavourite:
// different data depending on toggle state
return null;
case FullscreenAction.delete:
return AIcons.delete;
case FullscreenAction.info:
return OMIcons.info;
case FullscreenAction.rename:
return OMIcons.title;
case FullscreenAction.rotateCCW:
return AIcons.rotateLeft;
case FullscreenAction.rotateCW:
return AIcons.rotateRight;
case FullscreenAction.print:
return AIcons.print;
case FullscreenAction.share:
return AIcons.share;
// external app actions
case FullscreenAction.edit:
case FullscreenAction.open:
case FullscreenAction.setAs:
case FullscreenAction.openMap:
return null;
case FullscreenAction.debug:
return OMIcons.whatshot;
}
return null;
}
}

View file

@ -9,7 +9,7 @@ import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/album/collection_page.dart';
import 'package:aves/widgets/common/image_providers/thumbnail_provider.dart';
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
import 'package:aves/widgets/fullscreen/fullscreen_action_delegate.dart';
import 'package:aves/widgets/common/entry_action_delegate.dart';
import 'package:aves/widgets/fullscreen/image_page.dart';
import 'package:aves/widgets/fullscreen/info/info_page.dart';
import 'package:aves/widgets/fullscreen/overlay/bottom.dart';
@ -49,7 +49,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
Animation<double> _topOverlayScale, _bottomOverlayScale;
Animation<Offset> _bottomOverlayOffset;
EdgeInsets _frozenViewInsets, _frozenViewPadding;
FullscreenActionDelegate _actionDelegate;
EntryActionDelegate _actionDelegate;
final List<Tuple2<String, IjkMediaController>> _videoControllers = [];
CollectionLens get collection => widget.collection;
@ -93,7 +93,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
curve: Curves.easeOutQuad,
));
_overlayVisible.addListener(_onOverlayVisibleChange);
_actionDelegate = FullscreenActionDelegate(
_actionDelegate = EntryActionDelegate(
collection: collection,
showInfo: () => _goToVerticalPage(infoPage),
);

View file

@ -1,10 +1,10 @@
import 'dart:math';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/common/entry_actions.dart';
import 'package:aves/widgets/common/fx/sweeper.dart';
import 'package:aves/widgets/common/icons.dart';
import 'package:aves/widgets/common/menu_row.dart';
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
import 'package:aves/widgets/fullscreen/overlay/common.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -15,7 +15,7 @@ class FullscreenTopOverlay extends StatelessWidget {
final int index;
final Animation<double> scale;
final EdgeInsets viewInsets, viewPadding;
final Function(FullscreenAction value) onActionSelected;
final Function(EntryAction value) onActionSelected;
final bool canToggleFavourite;
ImageEntry get entry => entries[index];
@ -53,12 +53,12 @@ class FullscreenTopOverlay extends StatelessWidget {
final quickActionCount = min(targetCount, availableCount);
final quickActions = [
FullscreenAction.toggleFavourite,
FullscreenAction.share,
FullscreenAction.delete,
EntryAction.toggleFavourite,
EntryAction.share,
EntryAction.delete,
].where(_canDo).take(quickActionCount);
final inAppActions = FullscreenActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo);
final externalAppActions = FullscreenActions.externalApp.where(_canDo);
final inAppActions = EntryActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo);
final externalAppActions = EntryActions.externalApp.where(_canDo);
return Row(
children: [
@ -70,14 +70,14 @@ class FullscreenTopOverlay extends StatelessWidget {
...quickActions.map(_buildOverlayButton),
OverlayButton(
scale: scale,
child: PopupMenuButton<FullscreenAction>(
child: PopupMenuButton<EntryAction>(
itemBuilder: (context) => [
...inAppActions.map(_buildPopupMenuItem),
const PopupMenuDivider(),
...externalAppActions.map(_buildPopupMenuItem),
if (kDebugMode) ...[
const PopupMenuDivider(),
_buildPopupMenuItem(FullscreenAction.debug),
_buildPopupMenuItem(EntryAction.debug),
]
],
onSelected: onActionSelected,
@ -93,37 +93,37 @@ class FullscreenTopOverlay extends StatelessWidget {
);
}
bool _canDo(FullscreenAction action) {
bool _canDo(EntryAction action) {
switch (action) {
case FullscreenAction.toggleFavourite:
case EntryAction.toggleFavourite:
return canToggleFavourite;
case FullscreenAction.delete:
case FullscreenAction.rename:
case EntryAction.delete:
case EntryAction.rename:
return entry.canEdit;
case FullscreenAction.rotateCCW:
case FullscreenAction.rotateCW:
case EntryAction.rotateCCW:
case EntryAction.rotateCW:
return entry.canRotate;
case FullscreenAction.print:
case EntryAction.print:
return entry.canPrint;
case FullscreenAction.openMap:
case EntryAction.openMap:
return entry.hasGps;
case FullscreenAction.share:
case FullscreenAction.info:
case FullscreenAction.open:
case FullscreenAction.edit:
case FullscreenAction.setAs:
case EntryAction.share:
case EntryAction.info:
case EntryAction.open:
case EntryAction.edit:
case EntryAction.setAs:
return true;
case FullscreenAction.debug:
case EntryAction.debug:
return kDebugMode;
}
return false;
}
Widget _buildOverlayButton(FullscreenAction action) {
Widget _buildOverlayButton(EntryAction action) {
Widget child;
final onPressed = () => onActionSelected?.call(action);
switch (action) {
case FullscreenAction.toggleFavourite:
case EntryAction.toggleFavourite:
child = ValueListenableBuilder<bool>(
valueListenable: entry.isFavouriteNotifier,
builder: (context, isFavourite, child) => Stack(
@ -142,24 +142,24 @@ class FullscreenTopOverlay extends StatelessWidget {
),
);
break;
case FullscreenAction.info:
case FullscreenAction.share:
case FullscreenAction.delete:
case FullscreenAction.rename:
case FullscreenAction.rotateCCW:
case FullscreenAction.rotateCW:
case FullscreenAction.print:
case EntryAction.info:
case EntryAction.share:
case EntryAction.delete:
case EntryAction.rename:
case EntryAction.rotateCCW:
case EntryAction.rotateCW:
case EntryAction.print:
child = IconButton(
icon: Icon(action.getIcon()),
onPressed: onPressed,
tooltip: action.getText(),
);
break;
case FullscreenAction.openMap:
case FullscreenAction.open:
case FullscreenAction.edit:
case FullscreenAction.setAs:
case FullscreenAction.debug:
case EntryAction.openMap:
case EntryAction.open:
case EntryAction.edit:
case EntryAction.setAs:
case EntryAction.debug:
break;
}
return child != null
@ -173,11 +173,11 @@ class FullscreenTopOverlay extends StatelessWidget {
: const SizedBox.shrink();
}
PopupMenuEntry<FullscreenAction> _buildPopupMenuItem(FullscreenAction action) {
PopupMenuEntry<EntryAction> _buildPopupMenuItem(EntryAction action) {
Widget child;
switch (action) {
// in app actions
case FullscreenAction.toggleFavourite:
case EntryAction.toggleFavourite:
child = entry.isFavouriteNotifier.value
? const MenuRow(
text: 'Remove from favourites',
@ -188,21 +188,21 @@ class FullscreenTopOverlay extends StatelessWidget {
icon: AIcons.favourite,
);
break;
case FullscreenAction.info:
case FullscreenAction.share:
case FullscreenAction.delete:
case FullscreenAction.rename:
case FullscreenAction.rotateCCW:
case FullscreenAction.rotateCW:
case FullscreenAction.print:
case FullscreenAction.debug:
case EntryAction.info:
case EntryAction.share:
case EntryAction.delete:
case EntryAction.rename:
case EntryAction.rotateCCW:
case EntryAction.rotateCW:
case EntryAction.print:
case EntryAction.debug:
child = MenuRow(text: action.getText(), icon: action.getIcon());
break;
// external app actions
case FullscreenAction.edit:
case FullscreenAction.open:
case FullscreenAction.setAs:
case FullscreenAction.openMap:
case EntryAction.edit:
case EntryAction.open:
case EntryAction.setAs:
case EntryAction.openMap:
child = Text(action.getText());
break;
}