viewer: collapse motion photo pages in button row

This commit is contained in:
Thibault Deckers 2022-03-06 12:27:05 +09:00
parent db5b971a81
commit e92cee781c
4 changed files with 54 additions and 17 deletions

View file

@ -15,7 +15,7 @@ class ThumbnailScroller extends StatefulWidget {
final ValueNotifier<int?> indexNotifier; final ValueNotifier<int?> indexNotifier;
final void Function(int index)? onTap; final void Function(int index)? onTap;
final Object? Function(AvesEntry entry)? heroTagger; final Object? Function(AvesEntry entry)? heroTagger;
final bool highlightable, showLocation; final bool scrollable, highlightable, showLocation;
const ThumbnailScroller({ const ThumbnailScroller({
Key? key, Key? key,
@ -27,6 +27,7 @@ class ThumbnailScroller extends StatefulWidget {
this.heroTagger, this.heroTagger,
this.highlightable = false, this.highlightable = false,
this.showLocation = true, this.showLocation = true,
this.scrollable = true,
}) : super(key: key); }) : super(key: key);
@override @override
@ -46,6 +47,10 @@ class _ThumbnailScrollerState extends State<ThumbnailScroller> {
ValueNotifier<int?> get indexNotifier => widget.indexNotifier; ValueNotifier<int?> get indexNotifier => widget.indexNotifier;
bool get scrollable => widget.scrollable;
static double widthFor(int pageCount) => pageCount == 0 ? 0 : pageCount * thumbnailExtent + (pageCount - 1) * separatorWidth;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -84,13 +89,14 @@ class _ThumbnailScrollerState extends State<ThumbnailScroller> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final marginWidth = max(0.0, (widget.availableWidth - thumbnailExtent) / 2 - separatorWidth); final marginWidth = max(0.0, (widget.availableWidth - thumbnailExtent) / 2 - separatorWidth);
final padding = EdgeInsets.only(left: marginWidth + separatorWidth, right: marginWidth); final padding = scrollable ? EdgeInsets.only(left: marginWidth + separatorWidth, right: marginWidth) : EdgeInsets.zero;
return GridTheme( return GridTheme(
extent: thumbnailExtent, extent: thumbnailExtent,
showLocation: widget.showLocation && settings.showThumbnailLocation, showLocation: widget.showLocation && settings.showThumbnailLocation,
showTrash: false, showTrash: false,
child: SizedBox( child: SizedBox(
width: scrollable ? null : widthFor(entryCount),
height: thumbnailExtent, height: thumbnailExtent,
child: ListView.builder( child: ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -98,10 +104,12 @@ class _ThumbnailScrollerState extends State<ThumbnailScroller> {
// as of Flutter v2.10.2, `FixedExtentScrollController` can only be used with `ListWheelScrollView` // as of Flutter v2.10.2, `FixedExtentScrollController` can only be used with `ListWheelScrollView`
// and `FixedExtentScrollPhysics` can only be used with Scrollables that uses the `FixedExtentScrollController` // and `FixedExtentScrollPhysics` can only be used with Scrollables that uses the `FixedExtentScrollController`
// so we use `KnownExtentScrollPhysics`, adapted from `FixedExtentScrollPhysics` without the constraints // so we use `KnownExtentScrollPhysics`, adapted from `FixedExtentScrollPhysics` without the constraints
physics: KnownExtentScrollPhysics( physics: scrollable
? KnownExtentScrollPhysics(
indexToScrollOffset: indexToScrollOffset, indexToScrollOffset: indexToScrollOffset,
scrollOffsetToIndex: scrollOffsetToIndex, scrollOffsetToIndex: scrollOffsetToIndex,
), )
: const NeverScrollableScrollPhysics(),
padding: padding, padding: padding,
itemExtent: itemExtent, itemExtent: itemExtent,
itemBuilder: (context, index) => _buildThumbnail(index), itemBuilder: (context, index) => _buildThumbnail(index),
@ -152,6 +160,8 @@ class _ThumbnailScrollerState extends State<ThumbnailScroller> {
} }
Future<void> _goTo(int index) async { Future<void> _goTo(int index) async {
if (!scrollable) return;
final targetOffset = indexToScrollOffset(index); final targetOffset = indexToScrollOffset(index);
final offsetDelta = (targetOffset - _scrollController.offset).abs(); final offsetDelta = (targetOffset - _scrollController.offset).abs();

View file

@ -136,13 +136,23 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
return Selector<MediaQueryData, double>( return Selector<MediaQueryData, double>(
selector: (context, mq) => mq.size.width, selector: (context, mq) => mq.size.width,
builder: (context, mqWidth, child) { builder: (context, mqWidth, child) {
final viewerButtonRow = ViewerButtonRow(
mainEntry: mainEntry,
pageEntry: pageEntry,
scale: _buttonScale,
canToggleFavourite: widget.hasCollection,
);
final showMultiPageOverlay = mainEntry.isMultiPage && multiPageController != null;
final collapsedPageScroller = mainEntry.isMotionPhoto;
return SizedBox( return SizedBox(
width: mqWidth, width: mqWidth,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (mainEntry.isMultiPage && multiPageController != null) if (showMultiPageOverlay && !collapsedPageScroller)
Padding( Padding(
padding: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.only(bottom: 8),
child: FadeTransition( child: FadeTransition(
@ -150,15 +160,30 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
child: MultiPageOverlay( child: MultiPageOverlay(
controller: multiPageController, controller: multiPageController,
availableWidth: mqWidth, availableWidth: mqWidth,
scrollable: true,
), ),
), ),
), ),
ViewerButtonRow( (showMultiPageOverlay && collapsedPageScroller)
mainEntry: mainEntry, ? Row(
pageEntry: pageEntry, crossAxisAlignment: CrossAxisAlignment.center,
scale: _buttonScale, children: [
canToggleFavourite: widget.hasCollection, SafeArea(
top: false,
bottom: false,
child: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: MultiPageOverlay(
controller: multiPageController,
availableWidth: mqWidth,
scrollable: false,
), ),
),
),
Expanded(child: viewerButtonRow),
],
)
: viewerButtonRow,
if (settings.showOverlayThumbnailPreview) if (settings.showOverlayThumbnailPreview)
FadeTransition( FadeTransition(
opacity: _thumbnailOpacity, opacity: _thumbnailOpacity,

View file

@ -8,11 +8,13 @@ import 'package:provider/provider.dart';
class MultiPageOverlay extends StatefulWidget { class MultiPageOverlay extends StatefulWidget {
final MultiPageController controller; final MultiPageController controller;
final double availableWidth; final double availableWidth;
final bool scrollable;
const MultiPageOverlay({ const MultiPageOverlay({
Key? key, Key? key,
required this.controller, required this.controller,
required this.availableWidth, required this.availableWidth,
required this.scrollable,
}) : super(key: key); }) : super(key: key);
@override @override
@ -66,6 +68,7 @@ class _MultiPageOverlayState extends State<MultiPageOverlay> {
entryCount: multiPageInfo?.pageCount ?? 0, entryCount: multiPageInfo?.pageCount ?? 0,
entryBuilder: (page) => multiPageInfo?.getPageEntryByIndex(page), entryBuilder: (page) => multiPageInfo?.getPageEntryByIndex(page),
indexNotifier: controller.pageNotifier, indexNotifier: controller.pageNotifier,
scrollable: widget.scrollable,
); );
}, },
); );

View file

@ -105,11 +105,10 @@ class ViewerButtonRow extends StatelessWidget {
return SafeArea( return SafeArea(
top: false, top: false,
bottom: false, bottom: false,
child: Selector<MediaQueryData, double>( child: LayoutBuilder(
selector: (context, mq) => mq.size.width - mq.padding.horizontal, builder: (context, constraints) {
builder: (context, mqWidth, child) {
final buttonWidth = OverlayButton.getSize(context); final buttonWidth = OverlayButton.getSize(context);
final availableCount = ((mqWidth - outerPadding * 2) / (buttonWidth + innerPadding)).floor(); final availableCount = ((constraints.maxWidth - outerPadding * 2) / (buttonWidth + innerPadding)).floor();
return Selector<Settings, bool>( return Selector<Settings, bool>(
selector: (context, s) => s.isRotationLocked, selector: (context, s) => s.isRotationLocked,
builder: (context, s, child) { builder: (context, s, child) {