tv: button focus style, stats page
This commit is contained in:
parent
96f72fcdb3
commit
b0f613db27
19 changed files with 448 additions and 242 deletions
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/actions/entry_set_actions.dart';
|
import 'package:aves/model/actions/entry_set_actions.dart';
|
||||||
|
@ -346,7 +345,13 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
].where(isVisible).map((action) {
|
].where(isVisible).map((action) {
|
||||||
final enabled = canApply(action);
|
final enabled = canApply(action);
|
||||||
return CaptionedButton(
|
return CaptionedButton(
|
||||||
iconButton: _buildButtonIcon(context, action, enabled: enabled, selection: selection),
|
iconButtonBuilder: (context, focusNode) => _buildButtonIcon(
|
||||||
|
context,
|
||||||
|
action,
|
||||||
|
enabled: enabled,
|
||||||
|
selection: selection,
|
||||||
|
focusNode: focusNode,
|
||||||
|
),
|
||||||
captionText: _buildButtonCaption(context, action, enabled: enabled),
|
captionText: _buildButtonCaption(context, action, enabled: enabled),
|
||||||
onPressed: enabled ? () => _onActionSelected(action) : null,
|
onPressed: enabled ? () => _onActionSelected(action) : null,
|
||||||
);
|
);
|
||||||
|
@ -433,6 +438,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
EntrySetAction action, {
|
EntrySetAction action, {
|
||||||
required bool enabled,
|
required bool enabled,
|
||||||
|
FocusNode? focusNode,
|
||||||
required Selection<AvesEntry> selection,
|
required Selection<AvesEntry> selection,
|
||||||
}) {
|
}) {
|
||||||
final onPressed = enabled ? () => _onActionSelected(action) : null;
|
final onPressed = enabled ? () => _onActionSelected(action) : null;
|
||||||
|
@ -445,12 +451,14 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
return TitleSearchToggler(
|
return TitleSearchToggler(
|
||||||
queryEnabled: queryEnabled,
|
queryEnabled: queryEnabled,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
focusNode: focusNode,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
case EntrySetAction.toggleFavourite:
|
case EntrySetAction.toggleFavourite:
|
||||||
return FavouriteToggler(
|
return FavouriteToggler(
|
||||||
entries: _getExpandedSelectedItems(selection),
|
entries: _getExpandedSelectedItems(selection),
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
|
@ -458,6 +466,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
key: _getActionKey(action),
|
key: _getActionKey(action),
|
||||||
icon: action.getIcon(),
|
icon: action.getIcon(),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: action.getText(context),
|
tooltip: action.getText(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -581,7 +590,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
void _onQueryFocusRequest() => _queryBarFocusNode.requestFocus();
|
void _onQueryFocusRequest() => _queryBarFocusNode.requestFocus();
|
||||||
|
|
||||||
void _updateStatusBarHeight() {
|
void _updateStatusBarHeight() {
|
||||||
_statusBarHeight = EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio).top;
|
_statusBarHeight = context.read<MediaQueryData>().padding.top;
|
||||||
_updateAppBarHeight();
|
_updateAppBarHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,14 @@ import 'package:provider/provider.dart';
|
||||||
abstract class ChooserQuickButton<T> extends StatefulWidget {
|
abstract class ChooserQuickButton<T> extends StatefulWidget {
|
||||||
final bool blurred;
|
final bool blurred;
|
||||||
final ValueSetter<T>? onChooserValue;
|
final ValueSetter<T>? onChooserValue;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
const ChooserQuickButton({
|
const ChooserQuickButton({
|
||||||
super.key,
|
super.key,
|
||||||
required this.blurred,
|
required this.blurred,
|
||||||
this.onChooserValue,
|
this.onChooserValue,
|
||||||
|
this.focusNode,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -71,6 +73,7 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
onPressed: widget.onPressed,
|
onPressed: widget.onPressed,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
tooltip: _hasChooser ? null : tooltip,
|
tooltip: _hasChooser ? null : tooltip,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ class RateButton extends ChooserQuickButton<int> {
|
||||||
super.key,
|
super.key,
|
||||||
required super.blurred,
|
required super.blurred,
|
||||||
super.onChooserValue,
|
super.onChooserValue,
|
||||||
|
super.focusNode,
|
||||||
required super.onPressed,
|
required super.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ class ShareButton extends ChooserQuickButton<ShareAction> {
|
||||||
required super.blurred,
|
required super.blurred,
|
||||||
required this.entries,
|
required this.entries,
|
||||||
super.onChooserValue,
|
super.onChooserValue,
|
||||||
|
super.focusNode,
|
||||||
required super.onPressed,
|
required super.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class TagButton extends ChooserQuickButton<CollectionFilter> {
|
||||||
super.key,
|
super.key,
|
||||||
required super.blurred,
|
required super.blurred,
|
||||||
super.onChooserValue,
|
super.onChooserValue,
|
||||||
|
super.focusNode,
|
||||||
required super.onPressed,
|
required super.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,14 @@ import 'package:provider/provider.dart';
|
||||||
class FavouriteToggler extends StatefulWidget {
|
class FavouriteToggler extends StatefulWidget {
|
||||||
final Set<AvesEntry> entries;
|
final Set<AvesEntry> entries;
|
||||||
final bool isMenuItem;
|
final bool isMenuItem;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
const FavouriteToggler({
|
const FavouriteToggler({
|
||||||
super.key,
|
super.key,
|
||||||
required this.entries,
|
required this.entries,
|
||||||
this.isMenuItem = false,
|
this.isMenuItem = false,
|
||||||
|
this.focusNode,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,6 +78,7 @@ class _FavouriteTogglerState extends State<FavouriteToggler> {
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(isFavourite ? isFavouriteIcon : isNotFavouriteIcon),
|
icon: Icon(isFavourite ? isFavouriteIcon : isNotFavouriteIcon),
|
||||||
onPressed: widget.onPressed,
|
onPressed: widget.onPressed,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite,
|
tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite,
|
||||||
),
|
),
|
||||||
Sweeper(
|
Sweeper(
|
||||||
|
|
|
@ -10,12 +10,14 @@ import 'package:flutter/material.dart';
|
||||||
class MuteToggler extends StatelessWidget {
|
class MuteToggler extends StatelessWidget {
|
||||||
final AvesVideoController? controller;
|
final AvesVideoController? controller;
|
||||||
final bool isMenuItem;
|
final bool isMenuItem;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
const MuteToggler({
|
const MuteToggler({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.isMenuItem = false,
|
this.isMenuItem = false,
|
||||||
|
this.focusNode,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ class MuteToggler extends StatelessWidget {
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
onPressed: canDo ? onPressed : null,
|
onPressed: canDo ? onPressed : null,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: text,
|
tooltip: text,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,12 +12,14 @@ import 'package:provider/provider.dart';
|
||||||
class PlayToggler extends StatefulWidget {
|
class PlayToggler extends StatefulWidget {
|
||||||
final AvesVideoController? controller;
|
final AvesVideoController? controller;
|
||||||
final bool isMenuItem;
|
final bool isMenuItem;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
const PlayToggler({
|
const PlayToggler({
|
||||||
super.key,
|
super.key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.isMenuItem = false,
|
this.isMenuItem = false,
|
||||||
|
this.focusNode,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -86,6 +88,7 @@ class _PlayTogglerState extends State<PlayToggler> with SingleTickerProviderStat
|
||||||
progress: _playPauseAnimation,
|
progress: _playPauseAnimation,
|
||||||
),
|
),
|
||||||
onPressed: widget.onPressed,
|
onPressed: widget.onPressed,
|
||||||
|
focusNode: widget.focusNode,
|
||||||
tooltip: text,
|
tooltip: text,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@ import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class TitleSearchToggler extends StatelessWidget {
|
class TitleSearchToggler extends StatelessWidget {
|
||||||
final bool queryEnabled, isMenuItem;
|
final bool queryEnabled, isMenuItem;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
const TitleSearchToggler({
|
const TitleSearchToggler({
|
||||||
super.key,
|
super.key,
|
||||||
required this.queryEnabled,
|
required this.queryEnabled,
|
||||||
this.isMenuItem = false,
|
this.isMenuItem = false,
|
||||||
|
this.focusNode,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ class TitleSearchToggler extends StatelessWidget {
|
||||||
: IconButton(
|
: IconButton(
|
||||||
icon: icon,
|
icon: icon,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: text,
|
tooltip: text,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,15 @@ class AvesBorder {
|
||||||
// 1 device pixel for curves is too thin
|
// 1 device pixel for curves is too thin
|
||||||
static double get curvedBorderWidth => window.devicePixelRatio > 2 ? 0.5 : 1.0;
|
static double get curvedBorderWidth => window.devicePixelRatio > 2 ? 0.5 : 1.0;
|
||||||
|
|
||||||
static BorderSide straightSide(BuildContext context) => BorderSide(
|
static BorderSide straightSide(BuildContext context, {double? width}) => BorderSide(
|
||||||
color: _borderColor(context),
|
color: _borderColor(context),
|
||||||
width: straightBorderWidth,
|
width: width ?? straightBorderWidth,
|
||||||
);
|
);
|
||||||
|
|
||||||
static BorderSide curvedSide(BuildContext context) => BorderSide(
|
static BorderSide curvedSide(BuildContext context, {double? width}) => BorderSide(
|
||||||
color: _borderColor(context),
|
color: _borderColor(context),
|
||||||
width: curvedBorderWidth,
|
width: width ?? curvedBorderWidth,
|
||||||
);
|
);
|
||||||
|
|
||||||
static Border border(BuildContext context) => Border.fromBorderSide(curvedSide(context));
|
static Border border(BuildContext context, {double? width}) => Border.fromBorderSide(curvedSide(context, width: width));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,58 +2,39 @@ import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
class CaptionedButton extends StatelessWidget {
|
typedef CaptionedIconButtonBuilder = Widget Function(BuildContext context, FocusNode focusNode);
|
||||||
|
|
||||||
|
class CaptionedButton extends StatefulWidget {
|
||||||
final Animation<double> scale;
|
final Animation<double> scale;
|
||||||
final Widget captionText;
|
final Widget captionText;
|
||||||
final Widget iconButton;
|
final CaptionedIconButtonBuilder iconButtonBuilder;
|
||||||
final bool showCaption;
|
final bool showCaption;
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
|
||||||
|
static const EdgeInsets padding = EdgeInsets.symmetric(horizontal: 8);
|
||||||
|
static const double iconTextPadding = 8;
|
||||||
|
|
||||||
CaptionedButton({
|
CaptionedButton({
|
||||||
super.key,
|
super.key,
|
||||||
this.scale = kAlwaysCompleteAnimation,
|
this.scale = kAlwaysCompleteAnimation,
|
||||||
Widget? icon,
|
Widget? icon,
|
||||||
Widget? iconButton,
|
CaptionedIconButtonBuilder? iconButtonBuilder,
|
||||||
String? caption,
|
String? caption,
|
||||||
Widget? captionText,
|
Widget? captionText,
|
||||||
this.showCaption = true,
|
this.showCaption = true,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
}) : assert(icon != null || iconButton != null),
|
}) : assert(icon != null || iconButtonBuilder != null),
|
||||||
assert(caption != null || captionText != null),
|
assert(caption != null || captionText != null),
|
||||||
iconButton = iconButton ?? IconButton(icon: icon!, onPressed: onPressed),
|
iconButtonBuilder = iconButtonBuilder ?? ((_, focusNode) => IconButton(icon: icon!, onPressed: onPressed, focusNode: focusNode)),
|
||||||
captionText = captionText ?? CaptionedButtonText(text: caption!, enabled: onPressed != null);
|
captionText = captionText ?? CaptionedButtonText(text: caption!, enabled: onPressed != null);
|
||||||
|
|
||||||
static const double padding = 8;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
State<CaptionedButton> createState() => _CaptionedButtonState();
|
||||||
return SizedBox(
|
|
||||||
width: _width(context),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
OverlayButton(
|
|
||||||
scale: scale,
|
|
||||||
child: iconButton,
|
|
||||||
),
|
|
||||||
if (showCaption) ...[
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
ScaleTransition(
|
|
||||||
scale: scale,
|
|
||||||
child: captionText,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: padding),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double _width(BuildContext context) => OverlayButton.getSize(context) + padding * 2;
|
static double getWidth(BuildContext context) => OverlayButton.getSize(context) + padding.horizontal;
|
||||||
|
|
||||||
static Size getSize(BuildContext context, String text, {required bool showCaption}) {
|
static Size getSize(BuildContext context, String text, {required bool showCaption}) {
|
||||||
final width = _width(context);
|
final width = getWidth(context);
|
||||||
var height = width;
|
var height = width;
|
||||||
if (showCaption) {
|
if (showCaption) {
|
||||||
final para = RenderParagraph(
|
final para = RenderParagraph(
|
||||||
|
@ -62,7 +43,7 @@ class CaptionedButton extends StatelessWidget {
|
||||||
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
textScaleFactor: MediaQuery.textScaleFactorOf(context),
|
||||||
maxLines: CaptionedButtonText.maxLines,
|
maxLines: CaptionedButtonText.maxLines,
|
||||||
)..layout(const BoxConstraints(), parentUsesSize: true);
|
)..layout(const BoxConstraints(), parentUsesSize: true);
|
||||||
height += para.getMaxIntrinsicHeight(width) + padding;
|
height += para.getMaxIntrinsicHeight(width) + padding.vertical;
|
||||||
}
|
}
|
||||||
return Size(width, height);
|
return Size(width, height);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +54,81 @@ class CaptionedButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _CaptionedButtonState extends State<CaptionedButton> {
|
||||||
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
final ValueNotifier<bool> _focusedNotifier = ValueNotifier(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_updateTraversal();
|
||||||
|
_focusNode.addListener(_onFocusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant CaptionedButton oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.onPressed != widget.onPressed) {
|
||||||
|
_updateTraversal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusNode.dispose();
|
||||||
|
_focusedNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: CaptionedButton.getWidth(context),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: CaptionedButton.padding.top),
|
||||||
|
OverlayButton(
|
||||||
|
scale: widget.scale,
|
||||||
|
focusNode: _focusNode,
|
||||||
|
child: widget.iconButtonBuilder(context, _focusNode),
|
||||||
|
),
|
||||||
|
if (widget.showCaption) ...[
|
||||||
|
const SizedBox(height: CaptionedButton.iconTextPadding),
|
||||||
|
ScaleTransition(
|
||||||
|
scale: widget.scale,
|
||||||
|
child: ValueListenableBuilder<bool>(
|
||||||
|
valueListenable: _focusedNotifier,
|
||||||
|
builder: (context, focused, child) {
|
||||||
|
final style = CaptionedButtonText.textStyle(context);
|
||||||
|
return AnimatedDefaultTextStyle(
|
||||||
|
style: focused
|
||||||
|
? style.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
)
|
||||||
|
: style,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: widget.captionText,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
SizedBox(height: CaptionedButton.padding.bottom),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFocusChanged() => _focusedNotifier.value = _focusNode.hasFocus;
|
||||||
|
|
||||||
|
void _updateTraversal() {
|
||||||
|
final enabled = widget.onPressed != null;
|
||||||
|
_focusNode.skipTraversal = !enabled;
|
||||||
|
_focusNode.canRequestFocus = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CaptionedButtonText extends StatelessWidget {
|
class CaptionedButtonText extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final bool enabled;
|
final bool enabled;
|
||||||
|
@ -87,7 +143,7 @@ class CaptionedButtonText extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var style = textStyle(context);
|
var style = DefaultTextStyle.of(context).style;
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
style = style.copyWith(color: style.color!.withOpacity(.2));
|
style = style.copyWith(color: style.color!.withOpacity(.2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,38 +4,92 @@ import 'package:aves/widgets/common/fx/blurred.dart';
|
||||||
import 'package:aves/widgets/common/fx/borders.dart';
|
import 'package:aves/widgets/common/fx/borders.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class OverlayButton extends StatelessWidget {
|
class OverlayButton extends StatefulWidget {
|
||||||
final Animation<double> scale;
|
final Animation<double> scale;
|
||||||
final BorderRadius? borderRadius;
|
final BorderRadius? borderRadius;
|
||||||
|
final FocusNode? focusNode;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const OverlayButton({
|
const OverlayButton({
|
||||||
super.key,
|
super.key,
|
||||||
this.scale = kAlwaysCompleteAnimation,
|
this.scale = kAlwaysCompleteAnimation,
|
||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
|
this.focusNode,
|
||||||
required this.child,
|
required this.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OverlayButton> createState() => _OverlayButtonState();
|
||||||
|
|
||||||
|
// icon (24) + icon padding (8) + button padding (16)
|
||||||
|
static double getSize(BuildContext context) => 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OverlayButtonState extends State<OverlayButton> {
|
||||||
|
final ValueNotifier<bool> _focusedNotifier = ValueNotifier(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_registerWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant OverlayButton oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
_unregisterWidget(oldWidget);
|
||||||
|
_registerWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_unregisterWidget(widget);
|
||||||
|
_focusedNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _registerWidget(OverlayButton widget) {
|
||||||
|
widget.focusNode?.addListener(_onFocusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _unregisterWidget(OverlayButton widget) {
|
||||||
|
widget.focusNode?.removeListener(_onFocusChanged);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final brightness = Theme.of(context).brightness;
|
final borderRadius = widget.borderRadius;
|
||||||
|
|
||||||
final blurred = settings.enableBlurEffect;
|
final blurred = settings.enableBlurEffect;
|
||||||
|
final overlayBackground = Themes.overlayBackgroundColor(
|
||||||
|
brightness: Theme.of(context).brightness,
|
||||||
|
blurred: blurred,
|
||||||
|
);
|
||||||
|
|
||||||
return ScaleTransition(
|
return ScaleTransition(
|
||||||
scale: scale,
|
scale: widget.scale,
|
||||||
child: borderRadius != null
|
child: ValueListenableBuilder<bool>(
|
||||||
|
valueListenable: _focusedNotifier,
|
||||||
|
builder: (context, focused, child) {
|
||||||
|
final border = AvesBorder.border(
|
||||||
|
context,
|
||||||
|
width: AvesBorder.curvedBorderWidth * (focused ? 2 : 1),
|
||||||
|
);
|
||||||
|
return borderRadius != null
|
||||||
? BlurredRRect(
|
? BlurredRRect(
|
||||||
enabled: blurred,
|
enabled: blurred,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.button,
|
type: MaterialType.button,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
color: Themes.overlayBackgroundColor(brightness: brightness, blurred: blurred),
|
color: overlayBackground,
|
||||||
child: Ink(
|
child: AnimatedContainer(
|
||||||
decoration: BoxDecoration(
|
foregroundDecoration: BoxDecoration(
|
||||||
border: AvesBorder.border(context),
|
border: border,
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
),
|
),
|
||||||
child: child,
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: widget.child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -43,21 +97,23 @@ class OverlayButton extends StatelessWidget {
|
||||||
enabled: blurred,
|
enabled: blurred,
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.circle,
|
type: MaterialType.circle,
|
||||||
color: Themes.overlayBackgroundColor(brightness: brightness, blurred: blurred),
|
color: overlayBackground,
|
||||||
child: Ink(
|
child: AnimatedContainer(
|
||||||
decoration: BoxDecoration(
|
foregroundDecoration: BoxDecoration(
|
||||||
border: AvesBorder.border(context),
|
border: border,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: child,
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: widget.child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// icon (24) + icon padding (8) + button padding (16) + border (1 or 2)
|
void _onFocusChanged() => _focusedNotifier.value = widget.focusNode?.hasFocus ?? false;
|
||||||
static double getSize(BuildContext context) => 48.0 + AvesBorder.curvedBorderWidth * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScalingOverlayTextButton extends StatelessWidget {
|
class ScalingOverlayTextButton extends StatelessWidget {
|
||||||
|
|
|
@ -288,7 +288,13 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
||||||
].where(isVisible).map((action) {
|
].where(isVisible).map((action) {
|
||||||
final enabled = canApply(action);
|
final enabled = canApply(action);
|
||||||
return CaptionedButton(
|
return CaptionedButton(
|
||||||
iconButton: _buildButtonIcon(context, actionDelegate, action, enabled: enabled),
|
iconButtonBuilder: (context, focusNode) => _buildButtonIcon(
|
||||||
|
context,
|
||||||
|
actionDelegate,
|
||||||
|
action,
|
||||||
|
enabled: enabled,
|
||||||
|
focusNode: focusNode,
|
||||||
|
),
|
||||||
captionText: _buildButtonCaption(context, action, enabled: enabled),
|
captionText: _buildButtonCaption(context, action, enabled: enabled),
|
||||||
onPressed: enabled ? () => _onActionSelected(context, action, actionDelegate) : null,
|
onPressed: enabled ? () => _onActionSelected(context, action, actionDelegate) : null,
|
||||||
);
|
);
|
||||||
|
@ -350,6 +356,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
||||||
CSAD actionDelegate,
|
CSAD actionDelegate,
|
||||||
ChipSetAction action, {
|
ChipSetAction action, {
|
||||||
required bool enabled,
|
required bool enabled,
|
||||||
|
FocusNode? focusNode,
|
||||||
}) {
|
}) {
|
||||||
final onPressed = enabled ? () => _onActionSelected(context, action, actionDelegate) : null;
|
final onPressed = enabled ? () => _onActionSelected(context, action, actionDelegate) : null;
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
@ -360,6 +367,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
||||||
builder: (context, queryEnabled, child) {
|
builder: (context, queryEnabled, child) {
|
||||||
return TitleSearchToggler(
|
return TitleSearchToggler(
|
||||||
queryEnabled: queryEnabled,
|
queryEnabled: queryEnabled,
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -368,6 +376,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: action.getIcon(),
|
icon: action.getIcon(),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: action.getText(context),
|
tooltip: action.getText(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,15 +57,23 @@ class _TvRailState extends State<TvRail> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_scrollController = ScrollController(initialScrollOffset: controller.offset);
|
_scrollController = ScrollController(initialScrollOffset: controller.offset);
|
||||||
_scrollController.addListener(_onScrollChanged);
|
_scrollController.addListener(_onScrollChanged);
|
||||||
|
|
||||||
|
_registerWidget(widget);
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _initFocus());
|
WidgetsBinding.instance.addPostFrameCallback((_) => _initFocus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant TvRail oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
_unregisterWidget(oldWidget);
|
||||||
|
_registerWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_unregisterWidget(widget);
|
||||||
_scrollController.removeListener(_onScrollChanged);
|
_scrollController.removeListener(_onScrollChanged);
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
_extendedNotifier.dispose();
|
_extendedNotifier.dispose();
|
||||||
|
@ -73,6 +81,14 @@ class _TvRailState extends State<TvRail> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _registerWidget(TvRail widget) {
|
||||||
|
widget.currentCollection?.filterChangeNotifier.addListener(_onCollectionFilterChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _unregisterWidget(TvRail widget) {
|
||||||
|
widget.currentCollection?.filterChangeNotifier.removeListener(_onCollectionFilterChanged);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final navEntries = _getNavEntries(context);
|
final navEntries = _getNavEntries(context);
|
||||||
|
@ -255,6 +271,8 @@ class _TvRailState extends State<TvRail> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onScrollChanged() => controller.offset = _scrollController.offset;
|
void _onScrollChanged() => controller.offset = _scrollController.offset;
|
||||||
|
|
||||||
|
void _onCollectionFilterChanged() => setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
|
|
|
@ -17,7 +17,8 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
|
||||||
final String Function(BuildContext context, T action) actionText;
|
final String Function(BuildContext context, T action) actionText;
|
||||||
|
|
||||||
static const double spacing = 8;
|
static const double spacing = 8;
|
||||||
static const padding = EdgeInsets.all(spacing);
|
static const double runSpacing = 20;
|
||||||
|
static const padding = EdgeInsets.symmetric(vertical: 16, horizontal: 8);
|
||||||
|
|
||||||
const AvailableActionPanel({
|
const AvailableActionPanel({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -56,7 +57,7 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
alignment: WrapAlignment.spaceEvenly,
|
alignment: WrapAlignment.spaceEvenly,
|
||||||
spacing: spacing,
|
spacing: spacing,
|
||||||
runSpacing: spacing,
|
runSpacing: runSpacing,
|
||||||
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);
|
||||||
|
@ -124,11 +125,10 @@ class AvailableActionPanel<T extends Object> extends StatelessWidget {
|
||||||
final buttonSizes = captions.map((v) => CaptionedButton.getSize(context, v, showCaption: true));
|
final buttonSizes = captions.map((v) => CaptionedButton.getSize(context, v, showCaption: true));
|
||||||
final actionsPerRun = (width - padding.horizontal + spacing) ~/ (buttonSizes.first.width + spacing);
|
final actionsPerRun = (width - padding.horizontal + spacing) ~/ (buttonSizes.first.width + spacing);
|
||||||
final runCount = (captions.length / actionsPerRun).ceil();
|
final runCount = (captions.length / actionsPerRun).ceil();
|
||||||
var height = .0;
|
var height = runSpacing * (runCount - 1) + padding.vertical / 2;
|
||||||
for (var i = 0; i < runCount; i++) {
|
for (var i = 0; i < runCount; i++) {
|
||||||
height += buttonSizes.skip(i * actionsPerRun).take(actionsPerRun).map((v) => v.height).max;
|
height += buttonSizes.skip(i * actionsPerRun).take(actionsPerRun).map((v) => v.height).max;
|
||||||
}
|
}
|
||||||
height += spacing * (runCount - 1) + padding.vertical;
|
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/settings/settings_definition.dart';
|
import 'package:aves/widgets/settings/settings_definition.dart';
|
||||||
import 'package:aves/widgets/settings/video/video.dart';
|
import 'package:aves/widgets/settings/video/video.dart';
|
||||||
|
@ -20,6 +21,7 @@ class _VideoSettingsPageState extends State<VideoSettingsPage> {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
automaticallyImplyLeading: !settings.useTvLayout,
|
||||||
title: Text(context.l10n.settingsVideoPageTitle),
|
title: Text(context.l10n.settingsVideoPageTitle),
|
||||||
),
|
),
|
||||||
body: Theme(
|
body: Theme(
|
||||||
|
|
|
@ -256,19 +256,7 @@ class _StatsPageState extends State<StatsPage> {
|
||||||
|
|
||||||
final totalEntryCount = entries.length;
|
final totalEntryCount = entries.length;
|
||||||
final hasMore = maxRowCount != null && entryCountMap.length > maxRowCount;
|
final hasMore = maxRowCount != null && entryCountMap.length > maxRowCount;
|
||||||
return [
|
final onHeaderPressed = hasMore
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: Constants.knownTitleTextStyle,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(AIcons.next),
|
|
||||||
onPressed: hasMore
|
|
||||||
? () => Navigator.push(
|
? () => Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
@ -287,12 +275,52 @@ class _StatsPageState extends State<StatsPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null;
|
||||||
|
Widget header = Text(
|
||||||
|
title,
|
||||||
|
style: Constants.knownTitleTextStyle,
|
||||||
|
);
|
||||||
|
if (settings.useTvLayout) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
header = Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
child: Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
|
child: InkResponse(
|
||||||
|
onTap: onHeaderPressed,
|
||||||
|
onHover: (_) {},
|
||||||
|
highlightShape: BoxShape.rectangle,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(123)),
|
||||||
|
containedInkWell: true,
|
||||||
|
splashColor: colors.primary.withOpacity(0.12),
|
||||||
|
hoverColor: colors.primary.withOpacity(0.04),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: header,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
header = Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
header,
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(AIcons.next),
|
||||||
|
onPressed: onHeaderPressed,
|
||||||
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
tooltip: MaterialLocalizations.of(context).moreButtonTooltip,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
header,
|
||||||
FilterTable(
|
FilterTable(
|
||||||
totalEntryCount: totalEntryCount,
|
totalEntryCount: totalEntryCount,
|
||||||
entryCountMap: entryCountMap,
|
entryCountMap: entryCountMap,
|
||||||
|
|
|
@ -87,7 +87,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
case EntryAction.print:
|
case EntryAction.print:
|
||||||
return device.canPrint && !targetEntry.isVideo;
|
return device.canPrint && !targetEntry.isVideo;
|
||||||
case EntryAction.openMap:
|
case EntryAction.openMap:
|
||||||
return targetEntry.hasGps;
|
return !settings.useTvLayout && targetEntry.hasGps;
|
||||||
case EntryAction.viewSource:
|
case EntryAction.viewSource:
|
||||||
return targetEntry.isSvg;
|
return targetEntry.isSvg;
|
||||||
case EntryAction.videoCaptureFrame:
|
case EntryAction.videoCaptureFrame:
|
||||||
|
@ -109,9 +109,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
case EntryAction.edit:
|
case EntryAction.edit:
|
||||||
return canWrite;
|
return canWrite;
|
||||||
case EntryAction.copyToClipboard:
|
case EntryAction.copyToClipboard:
|
||||||
|
case EntryAction.open:
|
||||||
return !settings.useTvLayout;
|
return !settings.useTvLayout;
|
||||||
case EntryAction.info:
|
case EntryAction.info:
|
||||||
case EntryAction.open:
|
|
||||||
case EntryAction.setAs:
|
case EntryAction.setAs:
|
||||||
case EntryAction.share:
|
case EntryAction.share:
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -121,13 +121,14 @@ class _TvButtonRowContent extends StatelessWidget {
|
||||||
final enabled = actionDelegate.canApply(action);
|
final enabled = actionDelegate.canApply(action);
|
||||||
return CaptionedButton(
|
return CaptionedButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
iconButton: _buildButtonIcon(
|
iconButtonBuilder: (context, focusNode) => ViewerButtonRowContent._buildButtonIcon(
|
||||||
context: context,
|
context: context,
|
||||||
action: action,
|
action: action,
|
||||||
mainEntry: mainEntry,
|
mainEntry: mainEntry,
|
||||||
pageEntry: pageEntry,
|
pageEntry: pageEntry,
|
||||||
videoController: videoController,
|
videoController: videoController,
|
||||||
actionDelegate: actionDelegate,
|
actionDelegate: actionDelegate,
|
||||||
|
focusNode: focusNode,
|
||||||
),
|
),
|
||||||
captionText: _buildButtonCaption(
|
captionText: _buildButtonCaption(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -144,6 +145,39 @@ class _TvButtonRowContent extends StatelessWidget {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Widget _buildButtonCaption({
|
||||||
|
required BuildContext context,
|
||||||
|
required EntryAction action,
|
||||||
|
required AvesEntry mainEntry,
|
||||||
|
required AvesEntry pageEntry,
|
||||||
|
required AvesVideoController? videoController,
|
||||||
|
required bool enabled,
|
||||||
|
}) {
|
||||||
|
switch (action) {
|
||||||
|
case EntryAction.toggleFavourite:
|
||||||
|
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
|
||||||
|
return FavouriteTogglerCaption(
|
||||||
|
entries: {favouriteTargetEntry},
|
||||||
|
enabled: enabled,
|
||||||
|
);
|
||||||
|
case EntryAction.videoToggleMute:
|
||||||
|
return MuteTogglerCaption(
|
||||||
|
controller: videoController,
|
||||||
|
enabled: enabled,
|
||||||
|
);
|
||||||
|
case EntryAction.videoTogglePlay:
|
||||||
|
return PlayTogglerCaption(
|
||||||
|
controller: videoController,
|
||||||
|
enabled: enabled,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return CaptionedButtonText(
|
||||||
|
text: action.getText(context),
|
||||||
|
enabled: enabled,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewerButtonRowContent extends StatelessWidget {
|
class ViewerButtonRowContent extends StatelessWidget {
|
||||||
|
@ -374,16 +408,16 @@ class ViewerButtonRowContent extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildButtonIcon({
|
static Widget _buildButtonIcon({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required EntryAction action,
|
required EntryAction action,
|
||||||
required AvesEntry mainEntry,
|
required AvesEntry mainEntry,
|
||||||
required AvesEntry pageEntry,
|
required AvesEntry pageEntry,
|
||||||
required AvesVideoController? videoController,
|
required AvesVideoController? videoController,
|
||||||
required EntryActionDelegate actionDelegate,
|
required EntryActionDelegate actionDelegate,
|
||||||
}) {
|
FocusNode? focusNode,
|
||||||
|
}) {
|
||||||
Widget? child;
|
Widget? child;
|
||||||
void onPressed() => actionDelegate.onActionSelected(context, action);
|
void onPressed() => actionDelegate.onActionSelected(context, action);
|
||||||
|
|
||||||
|
@ -393,6 +427,7 @@ Widget _buildButtonIcon({
|
||||||
builder: (context, canDo, child) => IconButton(
|
builder: (context, canDo, child) => IconButton(
|
||||||
icon: child!,
|
icon: child!,
|
||||||
onPressed: canDo ? onPressed : null,
|
onPressed: canDo ? onPressed : null,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: action.getText(context),
|
tooltip: action.getText(context),
|
||||||
),
|
),
|
||||||
child: action.getIcon(),
|
child: action.getIcon(),
|
||||||
|
@ -422,6 +457,7 @@ Widget _buildButtonIcon({
|
||||||
blurred: blurred,
|
blurred: blurred,
|
||||||
entries: {mainEntry},
|
entries: {mainEntry},
|
||||||
onChooserValue: (action) => actionDelegate.quickShare(context, action),
|
onChooserValue: (action) => actionDelegate.quickShare(context, action),
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -429,18 +465,21 @@ Widget _buildButtonIcon({
|
||||||
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
|
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
|
||||||
child = FavouriteToggler(
|
child = FavouriteToggler(
|
||||||
entries: {favouriteTargetEntry},
|
entries: {favouriteTargetEntry},
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case EntryAction.videoToggleMute:
|
case EntryAction.videoToggleMute:
|
||||||
child = MuteToggler(
|
child = MuteToggler(
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case EntryAction.videoTogglePlay:
|
case EntryAction.videoTogglePlay:
|
||||||
child = PlayToggler(
|
child = PlayToggler(
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -457,6 +496,7 @@ Widget _buildButtonIcon({
|
||||||
child = RateButton(
|
child = RateButton(
|
||||||
blurred: blurred,
|
blurred: blurred,
|
||||||
onChooserValue: (rating) => actionDelegate.quickRate(context, rating),
|
onChooserValue: (rating) => actionDelegate.quickRate(context, rating),
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -464,6 +504,7 @@ Widget _buildButtonIcon({
|
||||||
child = TagButton(
|
child = TagButton(
|
||||||
blurred: blurred,
|
blurred: blurred,
|
||||||
onChooserValue: (filter) => actionDelegate.quickTag(context, filter),
|
onChooserValue: (filter) => actionDelegate.quickTag(context, filter),
|
||||||
|
focusNode: focusNode,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -471,42 +512,11 @@ Widget _buildButtonIcon({
|
||||||
child = IconButton(
|
child = IconButton(
|
||||||
icon: action.getIcon(),
|
icon: action.getIcon(),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
|
focusNode: focusNode,
|
||||||
tooltip: action.getText(context),
|
tooltip: action.getText(context),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildButtonCaption({
|
|
||||||
required BuildContext context,
|
|
||||||
required EntryAction action,
|
|
||||||
required AvesEntry mainEntry,
|
|
||||||
required AvesEntry pageEntry,
|
|
||||||
required AvesVideoController? videoController,
|
|
||||||
required bool enabled,
|
|
||||||
}) {
|
|
||||||
switch (action) {
|
|
||||||
case EntryAction.toggleFavourite:
|
|
||||||
final favouriteTargetEntry = mainEntry.isBurst ? pageEntry : mainEntry;
|
|
||||||
return FavouriteTogglerCaption(
|
|
||||||
entries: {favouriteTargetEntry},
|
|
||||||
enabled: enabled,
|
|
||||||
);
|
|
||||||
case EntryAction.videoToggleMute:
|
|
||||||
return MuteTogglerCaption(
|
|
||||||
controller: videoController,
|
|
||||||
enabled: enabled,
|
|
||||||
);
|
|
||||||
case EntryAction.videoTogglePlay:
|
|
||||||
return PlayTogglerCaption(
|
|
||||||
controller: videoController,
|
|
||||||
enabled: enabled,
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return CaptionedButtonText(
|
|
||||||
text: action.getText(context),
|
|
||||||
enabled: enabled,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue