#1177 slideshow: fixed bottom overlay layout on inset transition
This commit is contained in:
parent
013b2631aa
commit
535d4c0d00
3 changed files with 144 additions and 72 deletions
|
@ -412,17 +412,17 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
}
|
||||
|
||||
Widget _buildSlideshowBottomOverlay(Size availableSize) {
|
||||
return SizedBox.fromSize(
|
||||
size: availableSize,
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: TooltipTheme(
|
||||
return TooltipTheme(
|
||||
data: TooltipTheme.of(context).copyWith(
|
||||
preferBelow: false,
|
||||
),
|
||||
child: SlideshowButtons(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: SlideshowBottomOverlay(
|
||||
animationController: _overlayAnimationController,
|
||||
),
|
||||
availableSize: availableSize,
|
||||
viewInsets: _frozenViewInsets,
|
||||
viewPadding: _frozenViewPadding,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -21,7 +21,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ViewerBottomOverlay extends StatefulWidget {
|
||||
class ViewerBottomOverlay extends StatelessWidget {
|
||||
final List<AvesEntry> entries;
|
||||
final int index;
|
||||
final CollectionLens? collection;
|
||||
|
@ -33,6 +33,10 @@ class ViewerBottomOverlay extends StatefulWidget {
|
|||
// always keep action buttons in the lower right corner, even with RTL locales
|
||||
static const actionsDirection = TextDirection.ltr;
|
||||
|
||||
AvesEntry? get entry {
|
||||
return index < entries.length ? entries[index] : null;
|
||||
}
|
||||
|
||||
const ViewerBottomOverlay({
|
||||
super.key,
|
||||
required this.entries,
|
||||
|
@ -45,27 +49,6 @@ class ViewerBottomOverlay extends StatefulWidget {
|
|||
required this.multiPageController,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ViewerBottomOverlayState();
|
||||
|
||||
static double actionSafeHeight(BuildContext context) {
|
||||
final mqPaddingBottom = context.select<MediaQueryData, double>((mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom));
|
||||
final buttonHeight = ViewerButtons.preferredHeight(context);
|
||||
final thumbnailHeight = (settings.showOverlayThumbnailPreview ? ViewerThumbnailPreview.preferredHeight : 0);
|
||||
return mqPaddingBottom + buttonHeight + thumbnailHeight;
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
||||
List<AvesEntry> get entries => widget.entries;
|
||||
|
||||
AvesEntry? get entry {
|
||||
final index = widget.index;
|
||||
return index < entries.length ? entries[index] : null;
|
||||
}
|
||||
|
||||
MultiPageController? get multiPageController => widget.multiPageController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mainEntry = entry;
|
||||
|
@ -73,15 +56,15 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
|||
|
||||
Widget _buildContent({AvesEntry? pageEntry}) => _BottomOverlayContent(
|
||||
entries: entries,
|
||||
index: widget.index,
|
||||
index: index,
|
||||
mainEntry: mainEntry,
|
||||
pageEntry: pageEntry ?? mainEntry,
|
||||
collection: widget.collection,
|
||||
availableSize: widget.availableSize,
|
||||
viewInsets: widget.viewInsets,
|
||||
viewPadding: widget.viewPadding,
|
||||
collection: collection,
|
||||
availableSize: availableSize,
|
||||
viewInsets: viewInsets,
|
||||
viewPadding: viewPadding,
|
||||
multiPageController: multiPageController,
|
||||
animationController: widget.animationController,
|
||||
animationController: animationController,
|
||||
);
|
||||
|
||||
Widget child = multiPageController != null
|
||||
|
@ -102,6 +85,13 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
|||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
static double actionSafeHeight(BuildContext context) {
|
||||
final mqPaddingBottom = context.select<MediaQueryData, double>((mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom));
|
||||
final buttonHeight = ViewerButtons.preferredHeight(context);
|
||||
final thumbnailHeight = (settings.showOverlayThumbnailPreview ? ViewerThumbnailPreview.preferredHeight : 0);
|
||||
return mqPaddingBottom + buttonHeight + thumbnailHeight;
|
||||
}
|
||||
}
|
||||
|
||||
class _BottomOverlayContent extends StatefulWidget {
|
||||
|
|
|
@ -1,21 +1,64 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
|
||||
import 'package:aves/widgets/viewer/controls/intents.dart';
|
||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/bottom.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
|
||||
import 'package:aves/widgets/viewer/slideshow_page.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SlideshowBottomOverlay extends StatelessWidget {
|
||||
final AnimationController animationController;
|
||||
final Size availableSize;
|
||||
final EdgeInsets? viewInsets, viewPadding;
|
||||
|
||||
const SlideshowBottomOverlay({
|
||||
super.key,
|
||||
required this.animationController,
|
||||
required this.availableSize,
|
||||
this.viewInsets,
|
||||
this.viewPadding,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<MediaQueryData, double>(
|
||||
selector: (context, mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom),
|
||||
builder: (context, mqPaddingBottom, child) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: mqPaddingBottom),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: SlideshowButtons(
|
||||
availableSize: availableSize,
|
||||
viewInsets: viewInsets,
|
||||
viewPadding: viewPadding,
|
||||
animationController: animationController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlideshowButtons extends StatefulWidget {
|
||||
final Size availableSize;
|
||||
final EdgeInsets? viewInsets, viewPadding;
|
||||
final AnimationController animationController;
|
||||
|
||||
const SlideshowButtons({
|
||||
super.key,
|
||||
required this.availableSize,
|
||||
required this.viewInsets,
|
||||
required this.viewPadding,
|
||||
required this.animationController,
|
||||
});
|
||||
|
||||
|
@ -70,7 +113,8 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FocusableActionDetector(
|
||||
final viewInsetsPadding = (widget.viewInsets ?? EdgeInsets.zero) + (widget.viewPadding ?? EdgeInsets.zero);
|
||||
final viewerButtonRow = FocusableActionDetector(
|
||||
focusNode: _buttonRowFocusScopeNode,
|
||||
shortcuts: settings.useTvLayout
|
||||
? const {
|
||||
|
@ -80,26 +124,53 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
actions: {
|
||||
TvShowLessInfoIntent: CallbackAction<Intent>(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
|
||||
},
|
||||
child: settings.useTvLayout
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _actions.map((action) {
|
||||
return CaptionedButton(
|
||||
scale: _buttonScale,
|
||||
icon: action.getIcon(),
|
||||
caption: action.getText(context),
|
||||
onPressed: () => _onAction(context, action),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
minimum: EdgeInsets.only(
|
||||
left: viewInsetsPadding.left,
|
||||
right: viewInsetsPadding.right,
|
||||
),
|
||||
child: _buildButtons(context),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: SafeArea(
|
||||
|
||||
final availableWidth = widget.availableSize.width;
|
||||
return SizedBox(
|
||||
width: availableWidth,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
viewerButtonRow,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildButtons(BuildContext context) {
|
||||
if (settings.useTvLayout) {
|
||||
return _buildTvButtonRowContent(context);
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: _padding / 2, right: _padding / 2, bottom: _padding),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _actions
|
||||
.map((action) => Padding(
|
||||
textDirection: ViewerBottomOverlay.actionsDirection,
|
||||
children: [
|
||||
const Spacer(),
|
||||
..._actions.map((action) => _buildOverlayButton(context, action)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOverlayButton(BuildContext context, SlideshowAction action) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: _padding / 2),
|
||||
child: OverlayButton(
|
||||
scale: _buttonScale,
|
||||
|
@ -109,11 +180,22 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
tooltip: action.getText(context),
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTvButtonRowContent(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
textDirection: ViewerBottomOverlay.actionsDirection,
|
||||
children: _actions.map((action) {
|
||||
return CaptionedButton(
|
||||
scale: _buttonScale,
|
||||
icon: action.getIcon(),
|
||||
caption: action.getText(context),
|
||||
onPressed: () => _onAction(context, action),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue