quick chooser changes
This commit is contained in:
parent
1e7690d5a4
commit
7cd2c3fa8b
7 changed files with 61 additions and 18 deletions
|
@ -37,7 +37,7 @@ class _MoveButtonState extends ChooserQuickButtonState<MoveButton, String> {
|
||||||
String get tooltip => action.getText(context);
|
String get tooltip => action.getText(context);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildChooser(Animation<double> animation) {
|
Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition) {
|
||||||
final options = settings.recentDestinationAlbums;
|
final options = settings.recentDestinationAlbums;
|
||||||
final takeCount = FilterQuickChooser.maxOptionCount - options.length;
|
final takeCount = FilterQuickChooser.maxOptionCount - options.length;
|
||||||
if (takeCount > 0) {
|
if (takeCount > 0) {
|
||||||
|
@ -53,9 +53,11 @@ class _MoveButtonState extends ChooserQuickButtonState<MoveButton, String> {
|
||||||
opacity: animation,
|
opacity: animation,
|
||||||
child: ScaleTransition(
|
child: ScaleTransition(
|
||||||
scale: animation,
|
scale: animation,
|
||||||
|
alignment: chooserPosition == PopupMenuPosition.over ? Alignment.bottomCenter : Alignment.topCenter,
|
||||||
child: AlbumQuickChooser(
|
child: AlbumQuickChooser(
|
||||||
valueNotifier: chooserValueNotifier,
|
valueNotifier: chooserValueNotifier,
|
||||||
options: widget.chooserPosition == PopupMenuPosition.over ? options.reversed.toList() : options,
|
options: options,
|
||||||
|
chooserPosition: chooserPosition,
|
||||||
pointerGlobalPosition: pointerGlobalPosition,
|
pointerGlobalPosition: pointerGlobalPosition,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,12 +10,14 @@ import 'package:provider/provider.dart';
|
||||||
class AlbumQuickChooser extends StatelessWidget {
|
class AlbumQuickChooser extends StatelessWidget {
|
||||||
final ValueNotifier<String?> valueNotifier;
|
final ValueNotifier<String?> valueNotifier;
|
||||||
final List<String> options;
|
final List<String> options;
|
||||||
|
final PopupMenuPosition chooserPosition;
|
||||||
final Stream<Offset> pointerGlobalPosition;
|
final Stream<Offset> pointerGlobalPosition;
|
||||||
|
|
||||||
const AlbumQuickChooser({
|
const AlbumQuickChooser({
|
||||||
super.key,
|
super.key,
|
||||||
required this.valueNotifier,
|
required this.valueNotifier,
|
||||||
required this.options,
|
required this.options,
|
||||||
|
required this.chooserPosition,
|
||||||
required this.pointerGlobalPosition,
|
required this.pointerGlobalPosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,6 +27,7 @@ class AlbumQuickChooser extends StatelessWidget {
|
||||||
return FilterQuickChooser<String>(
|
return FilterQuickChooser<String>(
|
||||||
valueNotifier: valueNotifier,
|
valueNotifier: valueNotifier,
|
||||||
options: options,
|
options: options,
|
||||||
|
chooserPosition: chooserPosition,
|
||||||
pointerGlobalPosition: pointerGlobalPosition,
|
pointerGlobalPosition: pointerGlobalPosition,
|
||||||
buildFilterChip: (context, album) => AvesFilterChip(
|
buildFilterChip: (context, album) => AvesFilterChip(
|
||||||
filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)),
|
filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)),
|
||||||
|
|
|
@ -35,7 +35,7 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
||||||
|
|
||||||
Curve get animationCurve => Curves.easeOutQuad;
|
Curve get animationCurve => Curves.easeOutQuad;
|
||||||
|
|
||||||
Widget buildChooser(Animation<double> animation);
|
Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition);
|
||||||
|
|
||||||
ValueNotifier<U?> get chooserValueNotifier => _chooserValueNotifier;
|
ValueNotifier<U?> get chooserValueNotifier => _chooserValueNotifier;
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
||||||
mediaQuery.padding,
|
mediaQuery.padding,
|
||||||
DisplayFeatureSubScreen.avoidBounds(mediaQuery).toSet(),
|
DisplayFeatureSubScreen.avoidBounds(mediaQuery).toSet(),
|
||||||
),
|
),
|
||||||
child: buildChooser(_animation!),
|
child: buildChooser(_animation!, chooserPosition),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,22 +1,29 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves_ui/aves_ui.dart';
|
import 'package:aves_ui/aves_ui.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class FilterQuickChooser<T> extends StatefulWidget {
|
class FilterQuickChooser<T> extends StatefulWidget {
|
||||||
final ValueNotifier<T?> valueNotifier;
|
final ValueNotifier<T?> valueNotifier;
|
||||||
final List<T> options;
|
final List<T> options;
|
||||||
|
final PopupMenuPosition chooserPosition;
|
||||||
final Stream<Offset> pointerGlobalPosition;
|
final Stream<Offset> pointerGlobalPosition;
|
||||||
final Widget Function(BuildContext context, T album) buildFilterChip;
|
final Widget Function(BuildContext context, T album) buildFilterChip;
|
||||||
|
|
||||||
static const int maxOptionCount = 3;
|
static const int maxOptionCount = 5;
|
||||||
|
|
||||||
FilterQuickChooser({
|
FilterQuickChooser({
|
||||||
super.key,
|
super.key,
|
||||||
required this.valueNotifier,
|
required this.valueNotifier,
|
||||||
required List<T> options,
|
required List<T> options,
|
||||||
|
required this.chooserPosition,
|
||||||
required this.pointerGlobalPosition,
|
required this.pointerGlobalPosition,
|
||||||
required this.buildFilterChip,
|
required this.buildFilterChip,
|
||||||
}) : options = options.take(maxOptionCount).toList();
|
}) : options = options.take(maxOptionCount).toList();
|
||||||
|
@ -33,13 +40,16 @@ class _FilterQuickChooserState<T> extends State<FilterQuickChooser<T>> {
|
||||||
|
|
||||||
List<T> get options => widget.options;
|
List<T> get options => widget.options;
|
||||||
|
|
||||||
|
bool get reversed => widget.chooserPosition == PopupMenuPosition.over;
|
||||||
|
|
||||||
static const margin = EdgeInsets.all(8);
|
static const margin = EdgeInsets.all(8);
|
||||||
static const padding = EdgeInsets.all(8);
|
static const padding = EdgeInsets.symmetric(horizontal: 8);
|
||||||
static const double intraPadding = 8;
|
static const double intraPadding = 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_selectedRowRect.value = Rect.fromLTWH(0, window.physicalSize.height * (reversed ? 1 : -1), 0, 0);
|
||||||
_registerWidget(widget);
|
_registerWidget(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,11 +82,38 @@ class _FilterQuickChooserState<T> extends State<FilterQuickChooser<T>> {
|
||||||
padding: margin,
|
padding: margin,
|
||||||
child: Material(
|
child: Material(
|
||||||
shape: AvesDialog.shape(context),
|
shape: AvesDialog.shape(context),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: ValueListenableBuilder<T?>(
|
child: ValueListenableBuilder<T?>(
|
||||||
valueListenable: valueNotifier,
|
valueListenable: valueNotifier,
|
||||||
builder: (context, selectedValue, child) {
|
builder: (context, selectedValue, child) {
|
||||||
|
final durations = context.watch<DurationsData>();
|
||||||
|
|
||||||
|
List<Widget> optionChildren = options.mapIndexed((index, value) {
|
||||||
|
final isFirst = index == (reversed ? options.length - 1 : 0);
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(top: isFirst ? intraPadding : 0, bottom: intraPadding),
|
||||||
|
child: widget.buildFilterChip(context, value),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
optionChildren = AnimationConfiguration.toStaggeredList(
|
||||||
|
duration: durations.staggeredAnimation * .5,
|
||||||
|
delay: durations.staggeredAnimationDelay * .5 * timeDilation,
|
||||||
|
childAnimationBuilder: (child) => SlideAnimation(
|
||||||
|
verticalOffset: 50.0,
|
||||||
|
child: FadeInAnimation(
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
children: optionChildren,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (reversed) {
|
||||||
|
optionChildren = optionChildren.reversed.toList();
|
||||||
|
}
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
ValueListenableBuilder<Rect>(
|
ValueListenableBuilder<Rect>(
|
||||||
|
@ -104,12 +141,7 @@ class _FilterQuickChooserState<T> extends State<FilterQuickChooser<T>> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: options.mapIndexed((index, value) {
|
children: optionChildren,
|
||||||
return Padding(
|
|
||||||
padding: index == 0 ? EdgeInsets.zero : const EdgeInsets.only(top: intraPadding),
|
|
||||||
child: widget.buildFilterChip(context, value),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -128,7 +160,7 @@ class _FilterQuickChooserState<T> extends State<FilterQuickChooser<T>> {
|
||||||
final contentHeight = chooserSize.height - (margin.vertical + padding.vertical);
|
final contentHeight = chooserSize.height - (margin.vertical + padding.vertical);
|
||||||
|
|
||||||
final optionCount = options.length;
|
final optionCount = options.length;
|
||||||
final itemHeight = (contentHeight - (optionCount - 1) * intraPadding) / optionCount;
|
final itemHeight = (contentHeight - (optionCount + 1) * intraPadding) / optionCount;
|
||||||
|
|
||||||
final local = chooserBox.globalToLocal(globalPosition);
|
final local = chooserBox.globalToLocal(globalPosition);
|
||||||
final dx = local.dx;
|
final dx = local.dx;
|
||||||
|
@ -138,8 +170,8 @@ class _FilterQuickChooserState<T> extends State<FilterQuickChooser<T>> {
|
||||||
if (0 < dx && dx < contentWidth && 0 < dy && dy < contentHeight) {
|
if (0 < dx && dx < contentWidth && 0 < dy && dy < contentHeight) {
|
||||||
final index = (optionCount * dy / contentHeight).floor();
|
final index = (optionCount * dy / contentHeight).floor();
|
||||||
if (0 <= index && index < optionCount) {
|
if (0 <= index && index < optionCount) {
|
||||||
selectedValue = options[index];
|
selectedValue = options[reversed ? optionCount - 1 - index : index];
|
||||||
final top = index * (itemHeight + intraPadding);
|
final top = index * (itemHeight + intraPadding) + intraPadding;
|
||||||
_selectedRowRect.value = Rect.fromLTWH(0, top, contentWidth, itemHeight);
|
_selectedRowRect.value = Rect.fromLTWH(0, top, contentWidth, itemHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@ import 'package:flutter/material.dart';
|
||||||
class TagQuickChooser extends StatelessWidget {
|
class TagQuickChooser extends StatelessWidget {
|
||||||
final ValueNotifier<CollectionFilter?> valueNotifier;
|
final ValueNotifier<CollectionFilter?> valueNotifier;
|
||||||
final List<CollectionFilter> options;
|
final List<CollectionFilter> options;
|
||||||
|
final PopupMenuPosition chooserPosition;
|
||||||
final Stream<Offset> pointerGlobalPosition;
|
final Stream<Offset> pointerGlobalPosition;
|
||||||
|
|
||||||
const TagQuickChooser({
|
const TagQuickChooser({
|
||||||
super.key,
|
super.key,
|
||||||
required this.valueNotifier,
|
required this.valueNotifier,
|
||||||
required this.options,
|
required this.options,
|
||||||
|
required this.chooserPosition,
|
||||||
required this.pointerGlobalPosition,
|
required this.pointerGlobalPosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,6 +24,7 @@ class TagQuickChooser extends StatelessWidget {
|
||||||
return FilterQuickChooser<CollectionFilter>(
|
return FilterQuickChooser<CollectionFilter>(
|
||||||
valueNotifier: valueNotifier,
|
valueNotifier: valueNotifier,
|
||||||
options: options,
|
options: options,
|
||||||
|
chooserPosition: chooserPosition,
|
||||||
pointerGlobalPosition: pointerGlobalPosition,
|
pointerGlobalPosition: pointerGlobalPosition,
|
||||||
buildFilterChip: (context, filter) => AvesFilterChip(
|
buildFilterChip: (context, filter) => AvesFilterChip(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
|
|
|
@ -28,11 +28,12 @@ class _RateButtonState extends ChooserQuickButtonState<RateButton, int> {
|
||||||
int? get defaultValue => 3;
|
int? get defaultValue => 3;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildChooser(Animation<double> animation) {
|
Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition) {
|
||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: animation,
|
opacity: animation,
|
||||||
child: ScaleTransition(
|
child: ScaleTransition(
|
||||||
scale: animation,
|
scale: animation,
|
||||||
|
alignment: chooserPosition == PopupMenuPosition.over ? Alignment.bottomCenter : Alignment.topCenter,
|
||||||
child: RateQuickChooser(
|
child: RateQuickChooser(
|
||||||
valueNotifier: chooserValueNotifier,
|
valueNotifier: chooserValueNotifier,
|
||||||
pointerGlobalPosition: pointerGlobalPosition,
|
pointerGlobalPosition: pointerGlobalPosition,
|
||||||
|
|
|
@ -34,7 +34,7 @@ class _TagButtonState extends ChooserQuickButtonState<TagButton, CollectionFilte
|
||||||
String get tooltip => action.getText(context);
|
String get tooltip => action.getText(context);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildChooser(Animation<double> animation) {
|
Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition) {
|
||||||
final options = settings.recentTags;
|
final options = settings.recentTags;
|
||||||
final takeCount = FilterQuickChooser.maxOptionCount - options.length;
|
final takeCount = FilterQuickChooser.maxOptionCount - options.length;
|
||||||
if (takeCount > 0) {
|
if (takeCount > 0) {
|
||||||
|
@ -50,9 +50,11 @@ class _TagButtonState extends ChooserQuickButtonState<TagButton, CollectionFilte
|
||||||
opacity: animation,
|
opacity: animation,
|
||||||
child: ScaleTransition(
|
child: ScaleTransition(
|
||||||
scale: animation,
|
scale: animation,
|
||||||
|
alignment: chooserPosition == PopupMenuPosition.over ? Alignment.bottomCenter : Alignment.topCenter,
|
||||||
child: TagQuickChooser(
|
child: TagQuickChooser(
|
||||||
valueNotifier: chooserValueNotifier,
|
valueNotifier: chooserValueNotifier,
|
||||||
options: widget.chooserPosition == PopupMenuPosition.over ? options.reversed.toList() : options,
|
options: options,
|
||||||
|
chooserPosition: chooserPosition,
|
||||||
pointerGlobalPosition: pointerGlobalPosition,
|
pointerGlobalPosition: pointerGlobalPosition,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue