Merge branch 'develop'
This commit is contained in:
commit
47f1e9253f
14 changed files with 411 additions and 208 deletions
|
@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [v1.4.2] - 2021-06-10
|
## [v1.4.3] - 2021-06-12
|
||||||
### Added
|
### Added
|
||||||
- Collection: snack bar action to show moved/copied/exported entries
|
- Collection: snack bar action to show moved/copied/exported entries
|
||||||
- Collection / Albums / Countries / Tags: when switching device orientation, keep items in view
|
- Collection / Albums / Countries / Tags: when switching device orientation, keep items in view
|
||||||
|
@ -23,6 +23,8 @@ All notable changes to this project will be documented in this file.
|
||||||
- fixed opening files shared via content URI with incorrect MIME type
|
- fixed opening files shared via content URI with incorrect MIME type
|
||||||
- refresh collection when entries modified in Viewer no longer match collection filters
|
- refresh collection when entries modified in Viewer no longer match collection filters
|
||||||
|
|
||||||
|
## [v1.4.2] - 2021-06-10 [YANKED]
|
||||||
|
|
||||||
## [v1.4.1] - 2021-04-29
|
## [v1.4.1] - 2021-04-29
|
||||||
### Added
|
### Added
|
||||||
- Motion photo support
|
- Motion photo support
|
||||||
|
|
|
@ -4,20 +4,24 @@ import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
enum EntryAction {
|
enum EntryAction {
|
||||||
delete,
|
delete,
|
||||||
edit,
|
|
||||||
export,
|
export,
|
||||||
flip,
|
|
||||||
info,
|
info,
|
||||||
open,
|
|
||||||
openMap,
|
|
||||||
print,
|
print,
|
||||||
rename,
|
rename,
|
||||||
rotateCCW,
|
|
||||||
rotateCW,
|
|
||||||
setAs,
|
|
||||||
share,
|
share,
|
||||||
toggleFavourite,
|
toggleFavourite,
|
||||||
|
// raster
|
||||||
|
rotateCCW,
|
||||||
|
rotateCW,
|
||||||
|
flip,
|
||||||
|
// vector
|
||||||
viewSource,
|
viewSource,
|
||||||
|
// external
|
||||||
|
edit,
|
||||||
|
open,
|
||||||
|
openMap,
|
||||||
|
setAs,
|
||||||
|
// debug
|
||||||
debug,
|
debug,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +59,6 @@ class EntryActions {
|
||||||
extension ExtraEntryAction on EntryAction {
|
extension ExtraEntryAction on EntryAction {
|
||||||
String getText(BuildContext context) {
|
String getText(BuildContext context) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
// in app actions
|
|
||||||
case EntryAction.toggleFavourite:
|
case EntryAction.toggleFavourite:
|
||||||
// different data depending on toggle state
|
// different data depending on toggle state
|
||||||
return context.l10n.entryActionAddFavourite;
|
return context.l10n.entryActionAddFavourite;
|
||||||
|
@ -67,19 +70,21 @@ extension ExtraEntryAction on EntryAction {
|
||||||
return context.l10n.entryActionInfo;
|
return context.l10n.entryActionInfo;
|
||||||
case EntryAction.rename:
|
case EntryAction.rename:
|
||||||
return context.l10n.entryActionRename;
|
return context.l10n.entryActionRename;
|
||||||
|
case EntryAction.print:
|
||||||
|
return context.l10n.entryActionPrint;
|
||||||
|
case EntryAction.share:
|
||||||
|
return context.l10n.entryActionShare;
|
||||||
|
// raster
|
||||||
case EntryAction.rotateCCW:
|
case EntryAction.rotateCCW:
|
||||||
return context.l10n.entryActionRotateCCW;
|
return context.l10n.entryActionRotateCCW;
|
||||||
case EntryAction.rotateCW:
|
case EntryAction.rotateCW:
|
||||||
return context.l10n.entryActionRotateCW;
|
return context.l10n.entryActionRotateCW;
|
||||||
case EntryAction.flip:
|
case EntryAction.flip:
|
||||||
return context.l10n.entryActionFlip;
|
return context.l10n.entryActionFlip;
|
||||||
case EntryAction.print:
|
// vector
|
||||||
return context.l10n.entryActionPrint;
|
|
||||||
case EntryAction.share:
|
|
||||||
return context.l10n.entryActionShare;
|
|
||||||
case EntryAction.viewSource:
|
case EntryAction.viewSource:
|
||||||
return context.l10n.entryActionViewSource;
|
return context.l10n.entryActionViewSource;
|
||||||
// external app actions
|
// external
|
||||||
case EntryAction.edit:
|
case EntryAction.edit:
|
||||||
return context.l10n.entryActionEdit;
|
return context.l10n.entryActionEdit;
|
||||||
case EntryAction.open:
|
case EntryAction.open:
|
||||||
|
@ -88,6 +93,7 @@ extension ExtraEntryAction on EntryAction {
|
||||||
return context.l10n.entryActionSetAs;
|
return context.l10n.entryActionSetAs;
|
||||||
case EntryAction.openMap:
|
case EntryAction.openMap:
|
||||||
return context.l10n.entryActionOpenMap;
|
return context.l10n.entryActionOpenMap;
|
||||||
|
// debug
|
||||||
case EntryAction.debug:
|
case EntryAction.debug:
|
||||||
return 'Debug';
|
return 'Debug';
|
||||||
}
|
}
|
||||||
|
@ -95,7 +101,6 @@ extension ExtraEntryAction on EntryAction {
|
||||||
|
|
||||||
IconData? getIcon() {
|
IconData? getIcon() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
// in app actions
|
|
||||||
case EntryAction.toggleFavourite:
|
case EntryAction.toggleFavourite:
|
||||||
// different data depending on toggle state
|
// different data depending on toggle state
|
||||||
return AIcons.favourite;
|
return AIcons.favourite;
|
||||||
|
@ -107,24 +112,27 @@ extension ExtraEntryAction on EntryAction {
|
||||||
return AIcons.info;
|
return AIcons.info;
|
||||||
case EntryAction.rename:
|
case EntryAction.rename:
|
||||||
return AIcons.rename;
|
return AIcons.rename;
|
||||||
|
case EntryAction.print:
|
||||||
|
return AIcons.print;
|
||||||
|
case EntryAction.share:
|
||||||
|
return AIcons.share;
|
||||||
|
// raster
|
||||||
case EntryAction.rotateCCW:
|
case EntryAction.rotateCCW:
|
||||||
return AIcons.rotateLeft;
|
return AIcons.rotateLeft;
|
||||||
case EntryAction.rotateCW:
|
case EntryAction.rotateCW:
|
||||||
return AIcons.rotateRight;
|
return AIcons.rotateRight;
|
||||||
case EntryAction.flip:
|
case EntryAction.flip:
|
||||||
return AIcons.flip;
|
return AIcons.flip;
|
||||||
case EntryAction.print:
|
// vector
|
||||||
return AIcons.print;
|
|
||||||
case EntryAction.share:
|
|
||||||
return AIcons.share;
|
|
||||||
case EntryAction.viewSource:
|
case EntryAction.viewSource:
|
||||||
return AIcons.vector;
|
return AIcons.vector;
|
||||||
// external app actions
|
// external
|
||||||
case EntryAction.edit:
|
case EntryAction.edit:
|
||||||
case EntryAction.open:
|
case EntryAction.open:
|
||||||
case EntryAction.setAs:
|
case EntryAction.setAs:
|
||||||
case EntryAction.openMap:
|
case EntryAction.openMap:
|
||||||
return null;
|
return null;
|
||||||
|
// debug
|
||||||
case EntryAction.debug:
|
case EntryAction.debug:
|
||||||
return AIcons.debug;
|
return AIcons.debug;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/theme/durations.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -123,7 +124,8 @@ abstract class SectionedListLayoutProvider<T> extends StatelessWidget {
|
||||||
);
|
);
|
||||||
children.add(animate ? _buildAnimation(itemGridIndex, item) : item);
|
children.add(animate ? _buildAnimation(itemGridIndex, item) : item);
|
||||||
}
|
}
|
||||||
return Wrap(
|
return _GridRow(
|
||||||
|
extent: tileExtent,
|
||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
@ -274,3 +276,128 @@ class SectionLayout {
|
||||||
@override
|
@override
|
||||||
String toString() => '$runtimeType#${shortHash(this)}{sectionKey=$sectionKey, firstIndex=$firstIndex, lastIndex=$lastIndex, minOffset=$minOffset, maxOffset=$maxOffset, headerExtent=$headerExtent, tileExtent=$tileExtent, spacing=$spacing}';
|
String toString() => '$runtimeType#${shortHash(this)}{sectionKey=$sectionKey, firstIndex=$firstIndex, lastIndex=$lastIndex, minOffset=$minOffset, maxOffset=$maxOffset, headerExtent=$headerExtent, tileExtent=$tileExtent, spacing=$spacing}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _GridRow extends MultiChildRenderObjectWidget {
|
||||||
|
final double extent, spacing;
|
||||||
|
|
||||||
|
_GridRow({
|
||||||
|
Key? key,
|
||||||
|
required this.extent,
|
||||||
|
required this.spacing,
|
||||||
|
required List<Widget> children,
|
||||||
|
}) : super(key: key, children: children);
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
|
return _RenderGridRow(
|
||||||
|
extent: extent,
|
||||||
|
spacing: spacing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, _RenderGridRow renderObject) {
|
||||||
|
renderObject.extent = extent;
|
||||||
|
renderObject.spacing = spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DoubleProperty('extent', extent));
|
||||||
|
properties.add(DoubleProperty('spacing', spacing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GridRowParentData extends ContainerBoxParentData<RenderBox> {}
|
||||||
|
|
||||||
|
class _RenderGridRow extends RenderBox with ContainerRenderObjectMixin<RenderBox, _GridRowParentData>, RenderBoxContainerDefaultsMixin<RenderBox, _GridRowParentData> {
|
||||||
|
_RenderGridRow({
|
||||||
|
List<RenderBox>? children,
|
||||||
|
required double extent,
|
||||||
|
required double spacing,
|
||||||
|
}) : _extent = extent,
|
||||||
|
_spacing = spacing {
|
||||||
|
addAll(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get extent => _extent;
|
||||||
|
double _extent;
|
||||||
|
|
||||||
|
set extent(double value) {
|
||||||
|
if (_extent == value) return;
|
||||||
|
_extent = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
double get spacing => _spacing;
|
||||||
|
double _spacing;
|
||||||
|
|
||||||
|
set spacing(double value) {
|
||||||
|
if (_spacing == value) return;
|
||||||
|
_spacing = value;
|
||||||
|
markNeedsLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setupParentData(RenderBox child) {
|
||||||
|
if (child.parentData is! _GridRowParentData) {
|
||||||
|
child.parentData = _GridRowParentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double get intrinsicWidth => extent * childCount + spacing * (childCount - 1);
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMinIntrinsicWidth(double height) => intrinsicWidth;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMaxIntrinsicWidth(double height) => intrinsicWidth;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMinIntrinsicHeight(double width) => extent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMaxIntrinsicHeight(double width) => extent;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
var child = firstChild;
|
||||||
|
if (child == null) {
|
||||||
|
size = constraints.smallest;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size = Size(constraints.maxWidth, extent);
|
||||||
|
final childConstraints = BoxConstraints.tight(Size(extent, extent));
|
||||||
|
var offset = Offset.zero;
|
||||||
|
while (child != null) {
|
||||||
|
child.layout(childConstraints, parentUsesSize: false);
|
||||||
|
final childParentData = child.parentData! as _GridRowParentData;
|
||||||
|
childParentData.offset = offset;
|
||||||
|
offset += Offset(extent + spacing, 0);
|
||||||
|
child = childParentData.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double? computeDistanceToActualBaseline(TextBaseline baseline) {
|
||||||
|
return defaultComputeDistanceToHighestActualBaseline(baseline);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||||
|
return defaultHitTestChildren(result, position: position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
defaultPaint(context, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DoubleProperty('extent', extent));
|
||||||
|
properties.add(DoubleProperty('spacing', spacing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
55
lib/widgets/settings/entry_actions_editor.dart
Normal file
55
lib/widgets/settings/entry_actions_editor.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:aves/model/actions/entry_actions.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/settings/quick_actions/editor_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class QuickEntryActionsTile extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(context.l10n.settingsViewerQuickActionsTile),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
settings: const RouteSettings(name: QuickEntryActionEditorPage.routeName),
|
||||||
|
builder: (context) => const QuickEntryActionEditorPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class QuickEntryActionEditorPage extends StatelessWidget {
|
||||||
|
static const routeName = '/settings/quick_entry_actions';
|
||||||
|
|
||||||
|
const QuickEntryActionEditorPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
static const allAvailableActions = [
|
||||||
|
EntryAction.info,
|
||||||
|
EntryAction.toggleFavourite,
|
||||||
|
EntryAction.share,
|
||||||
|
EntryAction.delete,
|
||||||
|
EntryAction.rename,
|
||||||
|
EntryAction.export,
|
||||||
|
EntryAction.print,
|
||||||
|
EntryAction.viewSource,
|
||||||
|
EntryAction.flip,
|
||||||
|
EntryAction.rotateCCW,
|
||||||
|
EntryAction.rotateCW,
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return QuickActionEditorPage<EntryAction>(
|
||||||
|
bannerText: context.l10n.settingsViewerQuickActionEditorBanner,
|
||||||
|
allAvailableActions: allAvailableActions,
|
||||||
|
actionIcon: (action) => action.getIcon(),
|
||||||
|
actionText: (context, action) => action.getText(context),
|
||||||
|
load: () => settings.viewerQuickActions.toList(),
|
||||||
|
save: (actions) => settings.viewerQuickActions = actions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
48
lib/widgets/settings/quick_actions/action_button.dart
Normal file
48
lib/widgets/settings/quick_actions/action_button.dart
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import 'package:aves/widgets/viewer/overlay/common.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ActionButton extends StatelessWidget {
|
||||||
|
final String text;
|
||||||
|
final IconData? icon;
|
||||||
|
final bool enabled, showCaption;
|
||||||
|
|
||||||
|
const ActionButton({
|
||||||
|
required this.text,
|
||||||
|
required this.icon,
|
||||||
|
this.enabled = true,
|
||||||
|
this.showCaption = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const padding = 8.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textStyle = Theme.of(context).textTheme.caption;
|
||||||
|
return SizedBox(
|
||||||
|
width: OverlayButton.getSize(context) + padding * 2,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: padding),
|
||||||
|
OverlayButton(
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(icon),
|
||||||
|
onPressed: enabled ? () {} : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (showCaption) ...[
|
||||||
|
const SizedBox(height: padding),
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style: enabled ? textStyle : textStyle!.copyWith(color: textStyle.color!.withOpacity(.2)),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
const SizedBox(height: padding),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
lib/widgets/settings/quick_actions/action_panel.dart
Normal file
33
lib/widgets/settings/quick_actions/action_panel.dart
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import 'package:aves/theme/durations.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ActionPanel extends StatelessWidget {
|
||||||
|
final bool highlight;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const ActionPanel({
|
||||||
|
this.highlight = false,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final color = highlight ? Theme.of(context).accentColor : Colors.blueGrey;
|
||||||
|
return AnimatedContainer(
|
||||||
|
foregroundDecoration: BoxDecoration(
|
||||||
|
color: color.withOpacity(.2),
|
||||||
|
border: Border.fromBorderSide(BorderSide(
|
||||||
|
color: color,
|
||||||
|
width: highlight ? 2 : 1,
|
||||||
|
)),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
duration: Durations.quickActionHighlightAnimation,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,42 +1,33 @@
|
||||||
import 'package:aves/model/actions/entry_actions.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/quick_actions/common.dart';
|
import 'package:aves/widgets/settings/quick_actions/action_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:aves/widgets/settings/quick_actions/placeholder.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class AvailableActionPanel extends StatelessWidget {
|
class AvailableActionPanel<T extends Object> extends StatelessWidget {
|
||||||
final List<EntryAction> quickActions;
|
final List<T> allActions, quickActions;
|
||||||
final Listenable quickActionsChangeNotifier;
|
final Listenable quickActionsChangeNotifier;
|
||||||
final ValueNotifier<bool> panelHighlight;
|
final ValueNotifier<bool> panelHighlight;
|
||||||
final ValueNotifier<EntryAction?> draggedQuickAction;
|
final ValueNotifier<T?> draggedQuickAction;
|
||||||
final ValueNotifier<EntryAction?> draggedAvailableAction;
|
final ValueNotifier<T?> draggedAvailableAction;
|
||||||
final bool Function(EntryAction? action) removeQuickAction;
|
final bool Function(T? action) removeQuickAction;
|
||||||
|
final IconData? Function(T action) actionIcon;
|
||||||
|
final String Function(BuildContext context, T action) actionText;
|
||||||
|
|
||||||
const AvailableActionPanel({
|
const AvailableActionPanel({
|
||||||
|
required this.allActions,
|
||||||
required this.quickActions,
|
required this.quickActions,
|
||||||
required this.quickActionsChangeNotifier,
|
required this.quickActionsChangeNotifier,
|
||||||
required this.panelHighlight,
|
required this.panelHighlight,
|
||||||
required this.draggedQuickAction,
|
required this.draggedQuickAction,
|
||||||
required this.draggedAvailableAction,
|
required this.draggedAvailableAction,
|
||||||
required this.removeQuickAction,
|
required this.removeQuickAction,
|
||||||
|
required this.actionIcon,
|
||||||
|
required this.actionText,
|
||||||
});
|
});
|
||||||
|
|
||||||
static const allActions = [
|
|
||||||
EntryAction.info,
|
|
||||||
EntryAction.toggleFavourite,
|
|
||||||
EntryAction.share,
|
|
||||||
EntryAction.delete,
|
|
||||||
EntryAction.rename,
|
|
||||||
EntryAction.export,
|
|
||||||
EntryAction.print,
|
|
||||||
EntryAction.viewSource,
|
|
||||||
EntryAction.flip,
|
|
||||||
EntryAction.rotateCCW,
|
|
||||||
EntryAction.rotateCW,
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DragTarget<EntryAction>(
|
return DragTarget<T>(
|
||||||
onWillAccept: (data) {
|
onWillAccept: (data) {
|
||||||
if (draggedQuickAction.value != null) {
|
if (draggedQuickAction.value != null) {
|
||||||
_setPanelHighlight(true);
|
_setPanelHighlight(true);
|
||||||
|
@ -61,15 +52,12 @@ class AvailableActionPanel extends StatelessWidget {
|
||||||
children: allActions.map((action) {
|
children: allActions.map((action) {
|
||||||
final dragged = action == draggedAvailableAction.value;
|
final dragged = action == draggedAvailableAction.value;
|
||||||
final enabled = dragged || !quickActions.contains(action);
|
final enabled = dragged || !quickActions.contains(action);
|
||||||
Widget child = ActionButton(
|
var child = _buildActionButton(context, action, enabled: enabled);
|
||||||
action: action,
|
|
||||||
enabled: enabled,
|
|
||||||
);
|
|
||||||
if (dragged) {
|
if (dragged) {
|
||||||
child = DraggedPlaceholder(child: child);
|
child = DraggedPlaceholder(child: child);
|
||||||
}
|
}
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
child = _buildDraggable(action, child);
|
child = _buildDraggable(context, action, child);
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
@ -80,14 +68,20 @@ class AvailableActionPanel extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDraggable(EntryAction action, Widget child) => LongPressDraggable<EntryAction>(
|
Widget _buildDraggable(
|
||||||
|
BuildContext context,
|
||||||
|
T action,
|
||||||
|
Widget child,
|
||||||
|
) =>
|
||||||
|
LongPressDraggable<T>(
|
||||||
data: action,
|
data: action,
|
||||||
maxSimultaneousDrags: 1,
|
maxSimultaneousDrags: 1,
|
||||||
onDragStarted: () => _setDraggedAvailableAction(action),
|
onDragStarted: () => _setDraggedAvailableAction(action),
|
||||||
onDragEnd: (details) => _setDraggedAvailableAction(null),
|
onDragEnd: (details) => _setDraggedAvailableAction(null),
|
||||||
feedback: MediaQueryDataProvider(
|
feedback: MediaQueryDataProvider(
|
||||||
child: ActionButton(
|
child: _buildActionButton(
|
||||||
action: action,
|
context,
|
||||||
|
action,
|
||||||
showCaption: false,
|
showCaption: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -95,9 +89,22 @@ class AvailableActionPanel extends StatelessWidget {
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
||||||
void _setDraggedQuickAction(EntryAction? action) => draggedQuickAction.value = action;
|
Widget _buildActionButton(
|
||||||
|
BuildContext context,
|
||||||
|
T action, {
|
||||||
|
bool enabled = true,
|
||||||
|
bool showCaption = true,
|
||||||
|
}) =>
|
||||||
|
ActionButton(
|
||||||
|
text: actionText(context, action),
|
||||||
|
icon: actionIcon(action),
|
||||||
|
enabled: enabled,
|
||||||
|
showCaption: showCaption,
|
||||||
|
);
|
||||||
|
|
||||||
void _setDraggedAvailableAction(EntryAction? action) => draggedAvailableAction.value = action;
|
void _setDraggedQuickAction(T? action) => draggedQuickAction.value = action;
|
||||||
|
|
||||||
|
void _setDraggedAvailableAction(T? action) => draggedAvailableAction.value = action;
|
||||||
|
|
||||||
void _setPanelHighlight(bool flag) => panelHighlight.value = flag;
|
void _setPanelHighlight(bool flag) => panelHighlight.value = flag;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
import 'package:aves/model/actions/entry_actions.dart';
|
|
||||||
import 'package:aves/theme/durations.dart';
|
|
||||||
import 'package:aves/widgets/viewer/overlay/common.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class ActionPanel extends StatelessWidget {
|
|
||||||
final bool highlight;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const ActionPanel({
|
|
||||||
this.highlight = false,
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final color = highlight ? Theme.of(context).accentColor : Colors.blueGrey;
|
|
||||||
return AnimatedContainer(
|
|
||||||
foregroundDecoration: BoxDecoration(
|
|
||||||
color: color.withOpacity(.2),
|
|
||||||
border: Border.fromBorderSide(BorderSide(
|
|
||||||
color: color,
|
|
||||||
width: highlight ? 2 : 1,
|
|
||||||
)),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.all(16),
|
|
||||||
duration: Durations.quickActionHighlightAnimation,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
|
||||||
final EntryAction action;
|
|
||||||
final bool enabled, showCaption;
|
|
||||||
|
|
||||||
const ActionButton({
|
|
||||||
required this.action,
|
|
||||||
this.enabled = true,
|
|
||||||
this.showCaption = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
static const padding = 8.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final textStyle = Theme.of(context).textTheme.caption;
|
|
||||||
return SizedBox(
|
|
||||||
width: OverlayButton.getSize(context) + padding * 2,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
OverlayButton(
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon(action.getIcon()),
|
|
||||||
onPressed: enabled ? () {} : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (showCaption) ...[
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
Text(
|
|
||||||
action.getText(context),
|
|
||||||
style: enabled ? textStyle : textStyle!.copyWith(color: textStyle.color!.withOpacity(.2)),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DraggedPlaceholder extends StatelessWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const DraggedPlaceholder({
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: .2,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +1,47 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves/model/actions/entry_actions.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/utils/change_notifier.dart';
|
import 'package:aves/utils/change_notifier.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.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/quick_actions/action_button.dart';
|
||||||
|
import 'package:aves/widgets/settings/quick_actions/action_panel.dart';
|
||||||
import 'package:aves/widgets/settings/quick_actions/available_actions.dart';
|
import 'package:aves/widgets/settings/quick_actions/available_actions.dart';
|
||||||
import 'package:aves/widgets/settings/quick_actions/common.dart';
|
import 'package:aves/widgets/settings/quick_actions/placeholder.dart';
|
||||||
import 'package:aves/widgets/settings/quick_actions/quick_actions.dart';
|
import 'package:aves/widgets/settings/quick_actions/quick_actions.dart';
|
||||||
import 'package:aves/widgets/viewer/overlay/common.dart';
|
import 'package:aves/widgets/viewer/overlay/common.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class QuickActionsTile extends StatelessWidget {
|
class QuickActionEditorPage<T extends Object> extends StatefulWidget {
|
||||||
@override
|
final String bannerText;
|
||||||
Widget build(BuildContext context) {
|
final List<T> allAvailableActions;
|
||||||
return ListTile(
|
final IconData? Function(T action) actionIcon;
|
||||||
title: Text(context.l10n.settingsViewerQuickActionsTile),
|
final String Function(BuildContext context, T action) actionText;
|
||||||
onTap: () {
|
final List<T> Function() load;
|
||||||
Navigator.push(
|
final void Function(List<T> actions) save;
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
settings: const RouteSettings(name: QuickActionEditorPage.routeName),
|
|
||||||
builder: (context) => QuickActionEditorPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class QuickActionEditorPage extends StatefulWidget {
|
const QuickActionEditorPage({
|
||||||
static const routeName = '/settings/quick_actions';
|
required this.bannerText,
|
||||||
|
required this.allAvailableActions,
|
||||||
|
required this.actionIcon,
|
||||||
|
required this.actionText,
|
||||||
|
required this.load,
|
||||||
|
required this.save,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_QuickActionEditorPageState createState() => _QuickActionEditorPageState();
|
_QuickActionEditorPageState createState() => _QuickActionEditorPageState<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
class _QuickActionEditorPageState<T extends Object> extends State<QuickActionEditorPage<T>> {
|
||||||
final GlobalKey<AnimatedListState> _animatedListKey = GlobalKey(debugLabel: 'quick-actions-animated-list');
|
final GlobalKey<AnimatedListState> _animatedListKey = GlobalKey(debugLabel: 'quick-actions-animated-list');
|
||||||
Timer? _targetLeavingTimer;
|
Timer? _targetLeavingTimer;
|
||||||
late List<EntryAction> _quickActions;
|
late List<T> _quickActions;
|
||||||
final ValueNotifier<EntryAction?> _draggedQuickAction = ValueNotifier(null);
|
final ValueNotifier<T?> _draggedQuickAction = ValueNotifier(null);
|
||||||
final ValueNotifier<EntryAction?> _draggedAvailableAction = ValueNotifier(null);
|
final ValueNotifier<T?> _draggedAvailableAction = ValueNotifier(null);
|
||||||
final ValueNotifier<bool> _quickActionHighlight = ValueNotifier(false);
|
final ValueNotifier<bool> _quickActionHighlight = ValueNotifier(false);
|
||||||
final ValueNotifier<bool> _availableActionHighlight = ValueNotifier(false);
|
final ValueNotifier<bool> _availableActionHighlight = ValueNotifier(false);
|
||||||
final AChangeNotifier _quickActionsChangeNotifier = AChangeNotifier();
|
final AChangeNotifier _quickActionsChangeNotifier = AChangeNotifier();
|
||||||
|
@ -59,7 +55,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_quickActions = settings.viewerQuickActions.toList();
|
_quickActions = widget.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -79,7 +75,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final header = QuickActionButton(
|
final header = QuickActionButton<T>(
|
||||||
placement: QuickActionPlacement.header,
|
placement: QuickActionPlacement.header,
|
||||||
panelHighlight: _quickActionHighlight,
|
panelHighlight: _quickActionHighlight,
|
||||||
draggedQuickAction: _draggedQuickAction,
|
draggedQuickAction: _draggedQuickAction,
|
||||||
|
@ -88,7 +84,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
removeAction: _removeQuickAction,
|
removeAction: _removeQuickAction,
|
||||||
onTargetLeave: _onQuickActionTargetLeave,
|
onTargetLeave: _onQuickActionTargetLeave,
|
||||||
);
|
);
|
||||||
final footer = QuickActionButton(
|
final footer = QuickActionButton<T>(
|
||||||
placement: QuickActionPlacement.footer,
|
placement: QuickActionPlacement.footer,
|
||||||
panelHighlight: _quickActionHighlight,
|
panelHighlight: _quickActionHighlight,
|
||||||
draggedQuickAction: _draggedQuickAction,
|
draggedQuickAction: _draggedQuickAction,
|
||||||
|
@ -104,7 +100,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
),
|
),
|
||||||
body: WillPopScope(
|
body: WillPopScope(
|
||||||
onWillPop: () {
|
onWillPop: () {
|
||||||
settings.viewerQuickActions = _quickActions;
|
widget.save(_quickActions);
|
||||||
return SynchronousFuture(true);
|
return SynchronousFuture(true);
|
||||||
},
|
},
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
@ -116,7 +112,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
children: [
|
children: [
|
||||||
const Icon(AIcons.info),
|
const Icon(AIcons.info),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Expanded(child: Text(context.l10n.settingsViewerQuickActionEditorBanner)),
|
Expanded(child: Text(widget.bannerText)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -163,7 +159,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
itemBuilder: (context, index, animation) {
|
itemBuilder: (context, index, animation) {
|
||||||
if (index >= _quickActions.length) return const SizedBox();
|
if (index >= _quickActions.length) return const SizedBox();
|
||||||
final action = _quickActions[index];
|
final action = _quickActions[index];
|
||||||
return QuickActionButton(
|
return QuickActionButton<T>(
|
||||||
placement: QuickActionPlacement.action,
|
placement: QuickActionPlacement.action,
|
||||||
action: action,
|
action: action,
|
||||||
panelHighlight: _quickActionHighlight,
|
panelHighlight: _quickActionHighlight,
|
||||||
|
@ -172,6 +168,11 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
insertAction: _insertQuickAction,
|
insertAction: _insertQuickAction,
|
||||||
removeAction: _removeQuickAction,
|
removeAction: _removeQuickAction,
|
||||||
onTargetLeave: _onQuickActionTargetLeave,
|
onTargetLeave: _onQuickActionTargetLeave,
|
||||||
|
draggableFeedbackBuilder: (action) => ActionButton(
|
||||||
|
text: widget.actionText(context, action),
|
||||||
|
icon: widget.actionIcon(action),
|
||||||
|
showCaption: false,
|
||||||
|
),
|
||||||
child: _buildQuickActionButton(action, animation),
|
child: _buildQuickActionButton(action, animation),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -205,13 +206,16 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
highlight: highlight,
|
highlight: highlight,
|
||||||
child: child!,
|
child: child!,
|
||||||
),
|
),
|
||||||
child: AvailableActionPanel(
|
child: AvailableActionPanel<T>(
|
||||||
|
allActions: widget.allAvailableActions,
|
||||||
quickActions: _quickActions,
|
quickActions: _quickActions,
|
||||||
quickActionsChangeNotifier: _quickActionsChangeNotifier,
|
quickActionsChangeNotifier: _quickActionsChangeNotifier,
|
||||||
panelHighlight: _availableActionHighlight,
|
panelHighlight: _availableActionHighlight,
|
||||||
draggedQuickAction: _draggedQuickAction,
|
draggedQuickAction: _draggedQuickAction,
|
||||||
draggedAvailableAction: _draggedAvailableAction,
|
draggedAvailableAction: _draggedAvailableAction,
|
||||||
removeQuickAction: _removeQuickAction,
|
removeQuickAction: _removeQuickAction,
|
||||||
|
actionIcon: widget.actionIcon,
|
||||||
|
actionText: widget.actionText,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -224,7 +228,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
|
|
||||||
void _stopLeavingTimer() => _targetLeavingTimer?.cancel();
|
void _stopLeavingTimer() => _targetLeavingTimer?.cancel();
|
||||||
|
|
||||||
bool _insertQuickAction(EntryAction action, QuickActionPlacement placement, EntryAction? overAction) {
|
bool _insertQuickAction(T action, QuickActionPlacement placement, T? overAction) {
|
||||||
_stopLeavingTimer();
|
_stopLeavingTimer();
|
||||||
if (_reordering) return false;
|
if (_reordering) return false;
|
||||||
|
|
||||||
|
@ -256,7 +260,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _removeQuickAction(EntryAction? action) {
|
bool _removeQuickAction(T? action) {
|
||||||
if (action == null || !_quickActions.contains(action)) return false;
|
if (action == null || !_quickActions.contains(action)) return false;
|
||||||
|
|
||||||
final index = _quickActions.indexOf(action);
|
final index = _quickActions.indexOf(action);
|
||||||
|
@ -270,7 +274,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQuickActionButton(EntryAction action, Animation<double> animation) {
|
Widget _buildQuickActionButton(T action, Animation<double> animation) {
|
||||||
animation = animation.drive(CurveTween(curve: Curves.easeInOut));
|
animation = animation.drive(CurveTween(curve: Curves.easeInOut));
|
||||||
Widget child = FadeTransition(
|
Widget child = FadeTransition(
|
||||||
opacity: animation,
|
opacity: animation,
|
||||||
|
@ -281,7 +285,7 @@ class _QuickActionEditorPageState extends State<QuickActionEditorPage> {
|
||||||
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(action.getIcon()),
|
icon: Icon(widget.actionIcon(action)),
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
),
|
),
|
||||||
),
|
),
|
17
lib/widgets/settings/quick_actions/placeholder.dart
Normal file
17
lib/widgets/settings/quick_actions/placeholder.dart
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class DraggedPlaceholder extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const DraggedPlaceholder({
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Opacity(
|
||||||
|
opacity: .2,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,18 @@
|
||||||
import 'package:aves/model/actions/entry_actions.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/quick_actions/common.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
enum QuickActionPlacement { header, action, footer }
|
enum QuickActionPlacement { header, action, footer }
|
||||||
|
|
||||||
class QuickActionButton extends StatelessWidget {
|
class QuickActionButton<T extends Object> extends StatelessWidget {
|
||||||
final QuickActionPlacement placement;
|
final QuickActionPlacement placement;
|
||||||
final EntryAction? action;
|
final T? action;
|
||||||
final ValueNotifier<bool> panelHighlight;
|
final ValueNotifier<bool> panelHighlight;
|
||||||
final ValueNotifier<EntryAction?> draggedQuickAction;
|
final ValueNotifier<T?> draggedQuickAction;
|
||||||
final ValueNotifier<EntryAction?> draggedAvailableAction;
|
final ValueNotifier<T?> draggedAvailableAction;
|
||||||
final bool Function(EntryAction action, QuickActionPlacement placement, EntryAction? overAction) insertAction;
|
final bool Function(T action, QuickActionPlacement placement, T? overAction) insertAction;
|
||||||
final bool Function(EntryAction action) removeAction;
|
final bool Function(T action) removeAction;
|
||||||
final VoidCallback onTargetLeave;
|
final VoidCallback onTargetLeave;
|
||||||
|
final Widget Function(T action)? draggableFeedbackBuilder;
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
|
|
||||||
const QuickActionButton({
|
const QuickActionButton({
|
||||||
|
@ -25,6 +24,7 @@ class QuickActionButton extends StatelessWidget {
|
||||||
required this.insertAction,
|
required this.insertAction,
|
||||||
required this.removeAction,
|
required this.removeAction,
|
||||||
required this.onTargetLeave,
|
required this.onTargetLeave,
|
||||||
|
this.draggableFeedbackBuilder,
|
||||||
this.child,
|
this.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ class QuickActionButton extends StatelessWidget {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
DragTarget<EntryAction> _buildDragTarget(Widget? child) {
|
DragTarget<T> _buildDragTarget(Widget? child) {
|
||||||
return DragTarget<EntryAction>(
|
return DragTarget<T>(
|
||||||
onWillAccept: (data) {
|
onWillAccept: (data) {
|
||||||
if (draggedQuickAction.value != null) {
|
if (draggedQuickAction.value != null) {
|
||||||
insertAction(draggedQuickAction.value!, placement, action);
|
insertAction(draggedQuickAction.value!, placement, action);
|
||||||
|
@ -56,7 +56,7 @@ class QuickActionButton extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDraggable(Widget child, EntryAction action) => LongPressDraggable(
|
Widget _buildDraggable(Widget child, T action) => LongPressDraggable(
|
||||||
data: action,
|
data: action,
|
||||||
maxSimultaneousDrags: 1,
|
maxSimultaneousDrags: 1,
|
||||||
onDragStarted: () => _setDraggedQuickAction(action),
|
onDragStarted: () => _setDraggedQuickAction(action),
|
||||||
|
@ -65,16 +65,13 @@ class QuickActionButton extends StatelessWidget {
|
||||||
onDraggableCanceled: (velocity, offset) => _setDraggedQuickAction(null),
|
onDraggableCanceled: (velocity, offset) => _setDraggedQuickAction(null),
|
||||||
onDragCompleted: () => _setDraggedQuickAction(null),
|
onDragCompleted: () => _setDraggedQuickAction(null),
|
||||||
feedback: MediaQueryDataProvider(
|
feedback: MediaQueryDataProvider(
|
||||||
child: ActionButton(
|
child: draggableFeedbackBuilder!(action),
|
||||||
action: action,
|
|
||||||
showCaption: false,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
childWhenDragging: child,
|
childWhenDragging: child,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
||||||
void _setDraggedQuickAction(EntryAction? action) => draggedQuickAction.value = action;
|
void _setDraggedQuickAction(T? action) => draggedQuickAction.value = action;
|
||||||
|
|
||||||
void _setPanelHighlight(bool flag) => panelHighlight.value = flag;
|
void _setPanelHighlight(bool flag) => panelHighlight.value = flag;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import 'package:aves/widgets/settings/access_grants.dart';
|
||||||
import 'package:aves/widgets/settings/entry_background.dart';
|
import 'package:aves/widgets/settings/entry_background.dart';
|
||||||
import 'package:aves/widgets/settings/hidden_filters.dart';
|
import 'package:aves/widgets/settings/hidden_filters.dart';
|
||||||
import 'package:aves/widgets/settings/language.dart';
|
import 'package:aves/widgets/settings/language.dart';
|
||||||
import 'package:aves/widgets/settings/quick_actions/editor.dart';
|
import 'package:aves/widgets/settings/entry_actions_editor.dart';
|
||||||
import 'package:decorated_icon/decorated_icon.dart';
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
|
@ -191,7 +191,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
expandedNotifier: _expandedNotifier,
|
expandedNotifier: _expandedNotifier,
|
||||||
showHighlight: false,
|
showHighlight: false,
|
||||||
children: [
|
children: [
|
||||||
QuickActionsTile(),
|
QuickEntryActionsTile(),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: settings.showOverlayMinimap,
|
value: settings.showOverlayMinimap,
|
||||||
onChanged: (v) => settings.showOverlayMinimap = v,
|
onChanged: (v) => settings.showOverlayMinimap = v,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: aves
|
name: aves
|
||||||
description: A visual media gallery and metadata explorer app.
|
description: A visual media gallery and metadata explorer app.
|
||||||
repository: https://github.com/deckerst/aves
|
repository: https://github.com/deckerst/aves
|
||||||
version: 1.4.2+46
|
version: 1.4.3+47
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Thanks for using Aves!
|
Thanks for using Aves!
|
||||||
v1.4.2:
|
v1.4.3:
|
||||||
- improved navigation usability
|
- improved navigation usability
|
||||||
- changed thumbnail layout
|
- changed thumbnail layout
|
||||||
- improved playing videos with non-square pixels
|
- improved playing videos with non-square pixels
|
||||||
|
|
Loading…
Reference in a new issue