settings: most recent fullscreen actions
This commit is contained in:
parent
dd4ac33b6d
commit
fd149c30b3
5 changed files with 109 additions and 75 deletions
|
@ -1,4 +1,5 @@
|
||||||
import 'package:aves/model/collection_lens.dart';
|
import 'package:aves/model/collection_lens.dart';
|
||||||
|
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -19,6 +20,7 @@ class Settings {
|
||||||
static const collectionSortFactorKey = 'collection_sort_factor';
|
static const collectionSortFactorKey = 'collection_sort_factor';
|
||||||
static const infoMapZoomKey = 'info_map_zoom';
|
static const infoMapZoomKey = 'info_map_zoom';
|
||||||
static const catalogTimeZoneKey = 'catalog_time_zone';
|
static const catalogTimeZoneKey = 'catalog_time_zone';
|
||||||
|
static const mostRecentFullscreenActionsKey = 'most_recent_fullscreen_actions';
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
prefs = await SharedPreferences.getInstance();
|
prefs = await SharedPreferences.getInstance();
|
||||||
|
@ -60,6 +62,17 @@ class Settings {
|
||||||
|
|
||||||
set collectionSortFactor(SortFactor newValue) => setAndNotify(collectionSortFactorKey, newValue.toString());
|
set collectionSortFactor(SortFactor newValue) => setAndNotify(collectionSortFactorKey, newValue.toString());
|
||||||
|
|
||||||
|
List<FullscreenAction> get mostRecentFullscreenActions => getEnumListOrDefault(
|
||||||
|
mostRecentFullscreenActionsKey,
|
||||||
|
[
|
||||||
|
FullscreenAction.toggleFavourite,
|
||||||
|
FullscreenAction.share,
|
||||||
|
FullscreenAction.delete,
|
||||||
|
],
|
||||||
|
FullscreenAction.values);
|
||||||
|
|
||||||
|
set mostRecentFullscreenActions(List<FullscreenAction> newValue) => setAndNotify(mostRecentFullscreenActionsKey, newValue.map((v) => v.toString()).toList());
|
||||||
|
|
||||||
// convenience methods
|
// convenience methods
|
||||||
|
|
||||||
bool getBoolOrDefault(String key, bool defaultValue) => prefs.getKeys().contains(key) ? prefs.getBool(key) : defaultValue;
|
bool getBoolOrDefault(String key, bool defaultValue) => prefs.getKeys().contains(key) ? prefs.getBool(key) : defaultValue;
|
||||||
|
@ -74,6 +87,10 @@ class Settings {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<T> getEnumListOrDefault<T>(String key, List<T> defaultValue, Iterable<T> values) {
|
||||||
|
return prefs.getStringList(key)?.map((s) => values.firstWhere((el) => el.toString() == s, orElse: () => null))?.where((el) => el != null)?.toList() ?? defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
void setAndNotify(String key, dynamic newValue) {
|
void setAndNotify(String key, dynamic newValue) {
|
||||||
var oldValue = prefs.get(key);
|
var oldValue = prefs.get(key);
|
||||||
if (newValue == null) {
|
if (newValue == null) {
|
||||||
|
@ -81,6 +98,9 @@ class Settings {
|
||||||
} else if (newValue is String) {
|
} else if (newValue is String) {
|
||||||
oldValue = prefs.getString(key);
|
oldValue = prefs.getString(key);
|
||||||
prefs.setString(key, newValue);
|
prefs.setString(key, newValue);
|
||||||
|
} else if (newValue is List<String>) {
|
||||||
|
oldValue = prefs.getStringList(key);
|
||||||
|
prefs.setStringList(key, newValue);
|
||||||
} else if (newValue is int) {
|
} else if (newValue is int) {
|
||||||
oldValue = prefs.getInt(key);
|
oldValue = prefs.getInt(key);
|
||||||
prefs.setInt(key, newValue);
|
prefs.setInt(key, newValue);
|
||||||
|
|
|
@ -51,6 +51,7 @@ class DebugPageState extends State<DebugPage> {
|
||||||
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
||||||
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
||||||
Text('infoMapZoom: ${settings.infoMapZoom}'),
|
Text('infoMapZoom: ${settings.infoMapZoom}'),
|
||||||
|
Text('mostRecentFullscreenActions: ${settings.mostRecentFullscreenActions}'),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Text('Entries: ${entries.length}'),
|
Text('Entries: ${entries.length}'),
|
||||||
Text('Catalogued: ${catalogued.length}'),
|
Text('Catalogued: ${catalogued.length}'),
|
||||||
|
|
|
@ -4,14 +4,13 @@ import 'package:aves/model/collection_lens.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/utils/android_app_service.dart';
|
import 'package:aves/utils/android_app_service.dart';
|
||||||
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
|
import 'package:aves/widgets/common/image_providers/uri_image_provider.dart';
|
||||||
|
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||||
import 'package:flushbar/flushbar.dart';
|
import 'package:flushbar/flushbar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pdf/widgets.dart' as pdf;
|
import 'package:pdf/widgets.dart' as pdf;
|
||||||
import 'package:pedantic/pedantic.dart';
|
import 'package:pedantic/pedantic.dart';
|
||||||
import 'package:printing/printing.dart';
|
import 'package:printing/printing.dart';
|
||||||
|
|
||||||
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite }
|
|
||||||
|
|
||||||
class FullscreenActionDelegate {
|
class FullscreenActionDelegate {
|
||||||
final CollectionLens collection;
|
final CollectionLens collection;
|
||||||
final VoidCallback showInfo;
|
final VoidCallback showInfo;
|
||||||
|
|
55
lib/widgets/fullscreen/fullscreen_actions.dart
Normal file
55
lib/widgets/fullscreen/fullscreen_actions.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:outline_material_icons/outline_material_icons.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
enum FullscreenAction { delete, edit, info, open, openMap, print, rename, rotateCCW, rotateCW, setAs, share, toggleFavourite }
|
||||||
|
|
||||||
|
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,
|
||||||
|
];
|
||||||
|
|
||||||
|
static Tuple2<String, IconData> getTextIcon(FullscreenAction action) {
|
||||||
|
switch (action) {
|
||||||
|
// in app actions
|
||||||
|
case FullscreenAction.delete:
|
||||||
|
return const Tuple2('Delete', OMIcons.delete);
|
||||||
|
case FullscreenAction.info:
|
||||||
|
return const Tuple2('Info', OMIcons.info);
|
||||||
|
case FullscreenAction.rename:
|
||||||
|
return const Tuple2('Rename', OMIcons.title);
|
||||||
|
case FullscreenAction.rotateCCW:
|
||||||
|
return const Tuple2('Rotate left', OMIcons.rotateLeft);
|
||||||
|
case FullscreenAction.rotateCW:
|
||||||
|
return const Tuple2('Rotate right', OMIcons.rotateRight);
|
||||||
|
case FullscreenAction.print:
|
||||||
|
return const Tuple2('Print', OMIcons.print);
|
||||||
|
case FullscreenAction.share:
|
||||||
|
return const Tuple2('Share', OMIcons.share);
|
||||||
|
// external app actions
|
||||||
|
case FullscreenAction.edit:
|
||||||
|
return const Tuple2('Edit with…', null);
|
||||||
|
case FullscreenAction.open:
|
||||||
|
return const Tuple2('Open with…', null);
|
||||||
|
case FullscreenAction.setAs:
|
||||||
|
return const Tuple2('Set as…', null);
|
||||||
|
case FullscreenAction.openMap:
|
||||||
|
return const Tuple2('Show on map…', null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
|
import 'package:aves/model/settings.dart';
|
||||||
import 'package:aves/widgets/common/fx/sweeper.dart';
|
import 'package:aves/widgets/common/fx/sweeper.dart';
|
||||||
import 'package:aves/widgets/common/menu_row.dart';
|
import 'package:aves/widgets/common/menu_row.dart';
|
||||||
import 'package:aves/widgets/fullscreen/fullscreen_action_delegate.dart';
|
import 'package:aves/widgets/fullscreen/fullscreen_actions.dart';
|
||||||
import 'package:aves/widgets/fullscreen/overlay/common.dart';
|
import 'package:aves/widgets/fullscreen/overlay/common.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -22,6 +23,12 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
|
|
||||||
static const double padding = 8;
|
static const double padding = 8;
|
||||||
|
|
||||||
|
static const int landscapeActionCount = 3;
|
||||||
|
|
||||||
|
static const int portraitActionCount = 2;
|
||||||
|
|
||||||
|
static const List<FullscreenAction> possibleOverlayActions = FullscreenActions.inApp;
|
||||||
|
|
||||||
const FullscreenTopOverlay({
|
const FullscreenTopOverlay({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.entries,
|
@required this.entries,
|
||||||
|
@ -42,35 +49,15 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
child: Selector<MediaQueryData, Orientation>(
|
child: Selector<MediaQueryData, Orientation>(
|
||||||
selector: (c, mq) => mq.orientation,
|
selector: (c, mq) => mq.orientation,
|
||||||
builder: (c, orientation, child) {
|
builder: (c, orientation, child) {
|
||||||
final targetCount = orientation == Orientation.landscape ? 3 : 2;
|
final targetCount = orientation == Orientation.landscape ? landscapeActionCount : portraitActionCount;
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final availableCount = (constraints.maxWidth / (kMinInteractiveDimension + padding)).floor() - 2;
|
final availableCount = (constraints.maxWidth / (kMinInteractiveDimension + padding)).floor() - 2;
|
||||||
final recentActionCount = min(targetCount, availableCount);
|
final recentActionCount = min(targetCount, availableCount);
|
||||||
|
|
||||||
final recentActions = [
|
final recentActions = settings.mostRecentFullscreenActions.where(_canDo).take(recentActionCount);
|
||||||
FullscreenAction.toggleFavourite,
|
final inAppActions = FullscreenActions.inApp.where((action) => !recentActions.contains(action)).where(_canDo);
|
||||||
FullscreenAction.share,
|
final externalAppActions = FullscreenActions.externalApp.where(_canDo);
|
||||||
FullscreenAction.delete,
|
|
||||||
FullscreenAction.info,
|
|
||||||
FullscreenAction.rename,
|
|
||||||
].where(_canDo).take(recentActionCount);
|
|
||||||
final inAppActions = [
|
|
||||||
FullscreenAction.info,
|
|
||||||
FullscreenAction.toggleFavourite,
|
|
||||||
FullscreenAction.share,
|
|
||||||
FullscreenAction.delete,
|
|
||||||
FullscreenAction.rename,
|
|
||||||
FullscreenAction.rotateCCW,
|
|
||||||
FullscreenAction.rotateCW,
|
|
||||||
FullscreenAction.print,
|
|
||||||
].where((action) => !recentActions.contains(action)).where(_canDo);
|
|
||||||
final externalAppActions = [
|
|
||||||
FullscreenAction.edit,
|
|
||||||
FullscreenAction.open,
|
|
||||||
FullscreenAction.setAs,
|
|
||||||
FullscreenAction.openMap,
|
|
||||||
].where(_canDo);
|
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -88,7 +75,12 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
const PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
...externalAppActions.map(_buildPopupMenuItem),
|
...externalAppActions.map(_buildPopupMenuItem),
|
||||||
],
|
],
|
||||||
onSelected: onActionSelected,
|
onSelected: (action) {
|
||||||
|
if (possibleOverlayActions.contains(action)) {
|
||||||
|
settings.mostRecentFullscreenActions = [action, ...settings.mostRecentFullscreenActions].take(landscapeActionCount).toList();
|
||||||
|
}
|
||||||
|
onActionSelected?.call(action);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -148,37 +140,20 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FullscreenAction.share:
|
|
||||||
child = IconButton(
|
|
||||||
icon: const Icon(OMIcons.share),
|
|
||||||
onPressed: onPressed,
|
|
||||||
tooltip: 'Share',
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.delete:
|
|
||||||
child = IconButton(
|
|
||||||
icon: const Icon(OMIcons.delete),
|
|
||||||
onPressed: onPressed,
|
|
||||||
tooltip: 'Delete',
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.info:
|
case FullscreenAction.info:
|
||||||
child = IconButton(
|
case FullscreenAction.share:
|
||||||
icon: const Icon(OMIcons.info),
|
case FullscreenAction.delete:
|
||||||
onPressed: onPressed,
|
|
||||||
tooltip: 'Info',
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.rename:
|
case FullscreenAction.rename:
|
||||||
child = IconButton(
|
|
||||||
icon: const Icon(OMIcons.title),
|
|
||||||
onPressed: onPressed,
|
|
||||||
tooltip: 'Rename',
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.rotateCCW:
|
case FullscreenAction.rotateCCW:
|
||||||
case FullscreenAction.rotateCW:
|
case FullscreenAction.rotateCW:
|
||||||
case FullscreenAction.print:
|
case FullscreenAction.print:
|
||||||
|
final textIcon = FullscreenActions.getTextIcon(action);
|
||||||
|
child = IconButton(
|
||||||
|
icon: Icon(textIcon.item2),
|
||||||
|
onPressed: onPressed,
|
||||||
|
tooltip: textIcon.item1,
|
||||||
|
);
|
||||||
|
break;
|
||||||
case FullscreenAction.openMap:
|
case FullscreenAction.openMap:
|
||||||
case FullscreenAction.open:
|
case FullscreenAction.open:
|
||||||
case FullscreenAction.edit:
|
case FullscreenAction.edit:
|
||||||
|
@ -200,9 +175,6 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
Widget child;
|
Widget child;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
// in app actions
|
// in app actions
|
||||||
case FullscreenAction.info:
|
|
||||||
child = const MenuRow(text: 'Info', icon: OMIcons.info);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.toggleFavourite:
|
case FullscreenAction.toggleFavourite:
|
||||||
child = entry.isFavouriteNotifier.value
|
child = entry.isFavouriteNotifier.value
|
||||||
? const MenuRow(
|
? const MenuRow(
|
||||||
|
@ -214,36 +186,23 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
icon: OMIcons.favoriteBorder,
|
icon: OMIcons.favoriteBorder,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case FullscreenAction.info:
|
||||||
case FullscreenAction.share:
|
case FullscreenAction.share:
|
||||||
child = const MenuRow(text: 'Share', icon: OMIcons.share);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.delete:
|
case FullscreenAction.delete:
|
||||||
child = const MenuRow(text: 'Delete', icon: OMIcons.delete);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.rename:
|
case FullscreenAction.rename:
|
||||||
child = const MenuRow(text: 'Rename', icon: OMIcons.title);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.rotateCCW:
|
case FullscreenAction.rotateCCW:
|
||||||
child = const MenuRow(text: 'Rotate left', icon: OMIcons.rotateLeft);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.rotateCW:
|
case FullscreenAction.rotateCW:
|
||||||
child = const MenuRow(text: 'Rotate right', icon: OMIcons.rotateRight);
|
|
||||||
break;
|
|
||||||
case FullscreenAction.print:
|
case FullscreenAction.print:
|
||||||
child = const MenuRow(text: 'Print', icon: OMIcons.print);
|
final textIcon = FullscreenActions.getTextIcon(action);
|
||||||
|
child = MenuRow(text: textIcon.item1, icon: textIcon.item2);
|
||||||
break;
|
break;
|
||||||
// external app actions
|
// external app actions
|
||||||
case FullscreenAction.edit:
|
case FullscreenAction.edit:
|
||||||
child = const Text('Edit with…');
|
|
||||||
break;
|
|
||||||
case FullscreenAction.open:
|
case FullscreenAction.open:
|
||||||
child = const Text('Open with…');
|
|
||||||
break;
|
|
||||||
case FullscreenAction.setAs:
|
case FullscreenAction.setAs:
|
||||||
child = const Text('Set as…');
|
|
||||||
break;
|
|
||||||
case FullscreenAction.openMap:
|
case FullscreenAction.openMap:
|
||||||
child = const Text('Show on map…');
|
final textIcon = FullscreenActions.getTextIcon(action);
|
||||||
|
child = Text(textIcon.item1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return PopupMenuItem(
|
return PopupMenuItem(
|
||||||
|
|
Loading…
Reference in a new issue