#559 fixes for font scale

This commit is contained in:
Thibault Deckers 2023-03-19 19:38:07 +01:00
parent c6230df01d
commit 07e13bc8ac
18 changed files with 257 additions and 209 deletions

View file

@ -23,7 +23,6 @@ import 'package:aves/widgets/common/action_controls/togglers/favourite.dart';
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/container.dart';
import 'package:aves/widgets/common/basic/popup/expansion_panel.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
@ -145,7 +144,12 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
}
@override
void didChangeMetrics() => _updateStatusBarHeight();
void didChangeMetrics() {
// when top padding changes
_updateStatusBarHeight();
// when text scale factor changes
_updateAppBarHeight();
}
@override
Widget build(BuildContext context) {
@ -377,15 +381,12 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
final browsingQuickActions = settings.collectionBrowsingQuickActions;
final selectionQuickActions = isTrash ? [EntrySetAction.delete, EntrySetAction.restore] : settings.collectionSelectionQuickActions;
final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map(
(action) => FontSizeIconTheme(
child: _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
),
(action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
);
return [
...quickActionButtons,
FontSizeIconTheme(
child: PopupMenuButton<EntrySetAction>(
PopupMenuButton<EntrySetAction>(
// key is expected by test driver
key: const Key('appbar-menu-button'),
itemBuilder: (context) {
@ -437,7 +438,6 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
await _onActionSelected(action);
},
),
),
];
}

View file

@ -299,6 +299,18 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent
ScrollController get scrollController => widget.scrollController;
@override
void initState() {
super.initState();
_appBarHeightNotifier.addListener(_onAppBarHeightChanged);
}
@override
void dispose() {
_appBarHeightNotifier.removeListener(_onAppBarHeightChanged);
super.dispose();
}
@override
Widget build(BuildContext context) {
final scrollView = AnimationLimiter(
@ -339,6 +351,8 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent
child: selector,
);
}
void _onAppBarHeightChanged() => setState(() {});
}
class _CollectionScaler extends StatelessWidget {
@ -485,7 +499,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
return settings.useTvLayout ? scrollView : _buildDraggableScrollView(scrollView, widget.collection);
}
Widget _buildDraggableScrollView(ScrollView scrollView, CollectionLens collection) {
Widget _buildDraggableScrollView(Widget scrollView, CollectionLens collection) {
return ValueListenableBuilder<double>(
valueListenable: widget.appBarHeightNotifier,
builder: (context, appBarHeight, child) {
@ -550,7 +564,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
);
}
ScrollView _buildScrollView(Widget appBar, CollectionLens collection) {
Widget _buildScrollView(Widget appBar, CollectionLens collection) {
return CustomScrollView(
key: widget.scrollableKey,
primary: true,

View file

@ -62,9 +62,9 @@ class DraggableScrollbar extends StatefulWidget {
final double Function(double scrollOffset, double offsetIncrement)? dragOffsetSnapper;
/// The view that will be scrolled with the scroll thumb
final ScrollView child;
final Widget child;
DraggableScrollbar({
const DraggableScrollbar({
super.key,
required this.backgroundColor,
required this.scrollThumbSize,
@ -78,7 +78,7 @@ class DraggableScrollbar extends StatefulWidget {
required this.labelTextBuilder,
required this.crumbTextBuilder,
required this.child,
}) : assert(child.scrollDirection == Axis.vertical);
});
@override
State<DraggableScrollbar> createState() => _DraggableScrollbarState();

View file

@ -25,8 +25,6 @@ class _WheelSelectorState<T> extends State<WheelSelector<T>> {
late final FixedExtentScrollController _controller;
final ValueNotifier<bool> _focusedNotifier = ValueNotifier(false);
static const itemSize = Size(40, 40);
ValueNotifier<T> get valueNotifier => widget.valueNotifier;
List<T> get values => widget.values;
@ -51,6 +49,7 @@ class _WheelSelectorState<T> extends State<WheelSelector<T>> {
const background = Colors.transparent;
final foreground = DefaultTextStyle.of(context).style.color!;
final transitionDuration = context.select<DurationsData, Duration>((v) => v.formTransition);
final itemSize = Size.square(40 * context.select<MediaQueryData, double>((mq) => mq.textScaleFactor));
return FocusableActionDetector(
shortcuts: const {

View file

@ -130,7 +130,7 @@ class SectionHeader<T> extends StatelessWidget {
final para = RenderParagraph(
TextSpan(
children: [
// as of Flutter v2.8.1, `RenderParagraph` fails to lay out `WidgetSpan` offscreen
// as of Flutter v3.7.7, `RenderParagraph` fails to lay out `WidgetSpan` offscreen
// so we use a hair space times a magic number to match width
TextSpan(
// 23 hair spaces match a width of 40.0

View file

@ -1,6 +1,7 @@
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/insets.dart';
import 'package:aves/widgets/common/fx/blurred.dart';
import 'package:flutter/material.dart';
@ -65,8 +66,10 @@ class AvesAppBar extends StatelessWidget {
tag: leadingHeroTag,
flightShuttleBuilder: _flightShuttleBuilder,
transitionOnUserGestures: true,
child: FontSizeIconTheme(
child: leading!,
),
),
)
: const SizedBox(width: 16),
Expanded(
@ -78,6 +81,7 @@ class AvesAppBar extends StatelessWidget {
transitionOnUserGestures: true,
child: AnimatedSwitcher(
duration: context.read<DurationsData>().iconAnimation,
child: FontSizeIconTheme(
child: Row(
key: ValueKey(transitionKey),
children: [
@ -89,6 +93,7 @@ class AvesAppBar extends StatelessWidget {
),
),
),
),
],
),
),

View file

@ -13,7 +13,6 @@ import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
@ -210,16 +209,13 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
return [
if (widget.moveType != null)
..._quickActions.where(isVisible).map(
(action) => FontSizeIconTheme(
child: IconButton(
(action) => IconButton(
icon: action.getIcon(),
onPressed: () => onActionSelected(action),
tooltip: action.getText(context),
),
),
),
FontSizeIconTheme(
child: PopupMenuButton<ChipSetAction>(
PopupMenuButton<ChipSetAction>(
itemBuilder: (context) {
return _menuActions.where((v) => v == null || isVisible(v)).map((action) {
if (action == null) return const PopupMenuDivider();
@ -236,7 +232,6 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
onActionSelected(action);
},
),
),
];
}

View file

@ -2,6 +2,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/text_dropdown_button.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/transitions.dart';
@ -168,6 +169,7 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
final label = ConstrainedBox(
constraints: const BoxConstraints(minHeight: kMinInteractiveDimension),
child: FontSizeIconTheme(
child: Row(
children: [
Icon(icon),
@ -181,6 +183,7 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
if (trailing != null) trailing,
],
),
),
);
final selector = TextDropdownButton<T>(
values: options.map((v) => v.value).toList(),

View file

@ -11,7 +11,6 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_app_bar.dart';
@ -75,7 +74,7 @@ class FilterGridAppBar<T extends CollectionFilter, CSAD extends ChipSetActionDel
}
}
class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetActionDelegate<T>> extends State<FilterGridAppBar<T, CSAD>> with SingleTickerProviderStateMixin {
class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetActionDelegate<T>> extends State<FilterGridAppBar<T, CSAD>> with SingleTickerProviderStateMixin, WidgetsBindingObserver {
final List<StreamSubscription> _subscriptions = [];
late AnimationController _browseToSelectAnimation;
final ValueNotifier<bool> _isSelectingNotifier = ValueNotifier(false);
@ -105,6 +104,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
vsync: this,
);
_isSelectingNotifier.addListener(_onActivityChanged);
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) => _updateAppBarHeight());
}
@ -118,9 +118,16 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
_subscriptions
..forEach((sub) => sub.cancel())
..clear();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeMetrics() {
// when text scale factor changes
_updateAppBarHeight();
}
@override
Widget build(BuildContext context) {
final appMode = context.watch<ValueNotifier<AppMode>>().value;
@ -320,15 +327,12 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
final isSelecting = selection.isSelecting;
final quickActionButtons = (isSelecting ? selectionQuickActions : browsingQuickActions).where(isVisible).map(
(action) => FontSizeIconTheme(
child: _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
),
(action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
);
return [
...quickActionButtons,
FontSizeIconTheme(
child: PopupMenuButton<ChipSetAction>(
PopupMenuButton<ChipSetAction>(
itemBuilder: (context) {
final generalMenuItems = ChipSetActions.general.where(isVisible).map(
(action) => FilterGridAppBar.toMenuItem(context, action, enabled: canApply(action)),
@ -367,7 +371,6 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
_onActionSelected(context, action, actionDelegate);
},
),
),
];
}

View file

@ -485,9 +485,31 @@ class _FilterSectionedContentState<T extends CollectionFilter> extends State<_Fi
@override
void initState() {
super.initState();
_registerWidget(widget);
WidgetsBinding.instance.addPostFrameCallback((_) => _checkInitHighlight());
}
@override
void didUpdateWidget(covariant _FilterSectionedContent<T> oldWidget) {
super.didUpdateWidget(oldWidget);
_unregisterWidget(oldWidget);
_registerWidget(widget);
}
@override
void dispose() {
_unregisterWidget(widget);
super.dispose();
}
void _registerWidget(_FilterSectionedContent<T> widget) {
widget.appBarHeightNotifier.addListener(_onAppBarHeightChanged);
}
void _unregisterWidget(_FilterSectionedContent<T> widget) {
widget.appBarHeightNotifier.removeListener(_onAppBarHeightChanged);
}
@override
Widget build(BuildContext context) {
final scrollView = AnimationLimiter(
@ -527,6 +549,8 @@ class _FilterSectionedContentState<T extends CollectionFilter> extends State<_Fi
);
}
void _onAppBarHeightChanged() => setState(() {});
Future<void> _checkInitHighlight() async {
final highlightInfo = context.read<HighlightInfo>();
final filter = highlightInfo.clear();
@ -631,7 +655,7 @@ class _FilterScrollView<T extends CollectionFilter> extends StatelessWidget {
return settings.useTvLayout ? scrollView : _buildDraggableScrollView(scrollView);
}
Widget _buildDraggableScrollView(ScrollView scrollView) {
Widget _buildDraggableScrollView(Widget scrollView) {
return ValueListenableBuilder<double>(
valueListenable: appBarHeightNotifier,
builder: (context, appBarHeight, child) {
@ -672,7 +696,7 @@ class _FilterScrollView<T extends CollectionFilter> extends StatelessWidget {
);
}
ScrollView _buildScrollView(BuildContext context) {
Widget _buildScrollView(BuildContext context) {
return CustomScrollView(
key: scrollableKey,
controller: scrollController,

View file

@ -16,7 +16,6 @@ class MapInfoRow extends StatelessWidget {
final ValueNotifier<AvesEntry?> entryNotifier;
static const double iconPadding = 8.0;
static const double iconSize = 16.0;
static const double _interRowPadding = 2.0;
const MapInfoRow({
@ -66,6 +65,8 @@ class MapInfoRow extends StatelessWidget {
},
);
}
static double getIconSize(BuildContext context) => 16.0 * context.select<MediaQueryData, double>((mq) => mq.textScaleFactor);
}
class _AddressRow extends StatefulWidget {
@ -103,7 +104,7 @@ class _AddressRowState extends State<_AddressRow> {
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: MapInfoRow.iconPadding),
const Icon(AIcons.location, size: MapInfoRow.iconSize),
Icon(AIcons.location, size: MapInfoRow.getIconSize(context)),
const SizedBox(width: MapInfoRow.iconPadding),
Expanded(
child: Container(
@ -173,7 +174,7 @@ class _DateRow extends StatelessWidget {
return Row(
children: [
const SizedBox(width: MapInfoRow.iconPadding),
const Icon(AIcons.date, size: MapInfoRow.iconSize),
Icon(AIcons.date, size: MapInfoRow.getIconSize(context)),
const SizedBox(width: MapInfoRow.iconPadding),
Text(
dateText,

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves_utils/aves_utils.dart';
import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
@ -151,7 +152,7 @@ class _QuickActionEditorBodyState<T extends Object> extends State<QuickActionEdi
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(AIcons.info),
const FontSizeIconTheme(child: Icon(AIcons.info)),
const SizedBox(width: 16),
Expanded(child: Text(widget.bannerText)),
],

View file

@ -1,4 +1,5 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart';
@ -11,7 +12,7 @@ class DrawerEditorBanner extends StatelessWidget {
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(AIcons.info),
const FontSizeIconTheme(child: Icon(AIcons.info)),
const SizedBox(width: 16),
Expanded(child: Text(context.l10n.settingsNavigationDrawerBanner)),
],

View file

@ -1,5 +1,6 @@
import 'package:aves/services/common/services.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart';
@ -96,7 +97,7 @@ class _Header extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [
const Icon(AIcons.info),
const FontSizeIconTheme(child: Icon(AIcons.info)),
const SizedBox(width: 16),
Expanded(child: Text(context.l10n.settingsStorageAccessBanner)),
],

View file

@ -3,6 +3,7 @@ import 'package:aves/model/filters/path.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
@ -176,7 +177,7 @@ class _Banner extends StatelessWidget {
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(AIcons.info),
const FontSizeIconTheme(child: Icon(AIcons.info)),
const SizedBox(width: 16),
Expanded(child: Text(bannerText)),
],

View file

@ -128,8 +128,7 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
onPressed: () => _goToSearch(context),
tooltip: MaterialLocalizations.of(context).searchFieldLabel,
),
FontSizeIconTheme(
child: PopupMenuButton<SettingsAction>(
PopupMenuButton<SettingsAction>(
itemBuilder: (context) {
return [
PopupMenuItem(
@ -148,8 +147,7 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
_onActionSelected(action);
},
),
),
],
].map((v) => FontSizeIconTheme(child: v)).toList(),
),
body: GestureAreaProtectorStack(
child: SafeArea(

View file

@ -97,7 +97,7 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
// compute the size of keys and space in order to align values
final textScaleFactor = MediaQuery.textScaleFactorOf(context);
final keySizes = Map.fromEntries(keyValues.keys.map((key) => MapEntry(key, _getSpanWidth(TextSpan(text: key, style: _keyStyle), textScaleFactor))));
final keySizes = Map.fromEntries(keyValues.keys.map((key) => MapEntry(key, _getSpanWidth(TextSpan(text: _buildTextValue(key), style: _keyStyle), textScaleFactor))));
final lastKey = keyValues.keys.last;
return LayoutBuilder(
@ -115,15 +115,11 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
final spanBuilder = spanBuilders[key] ?? _buildTextValueSpans;
final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding;
// each text span embeds and pops a Bidi isolate,
// so that layout of the spans follows the directionality of the locale
// (e.g. keys on the right for RTL locale, whatever the key intrinsic directionality)
// and each span respects the directionality of its inner text only
return [
TextSpan(text: '${Constants.fsi}$key${Constants.pdi}', style: _keyStyle),
TextSpan(text: _buildTextValue(key), style: _keyStyle),
WidgetSpan(
child: SizedBox(
width: thisSpaceSize,
width: thisSpaceSize / textScaleFactor,
// as of Flutter v3.0.0, the underline decoration from the following `TextSpan`
// is applied to the `WidgetSpan` too, so we add a dummy `Text` as a workaround
child: const Text(''),
@ -161,8 +157,14 @@ class _InfoRowGroupState extends State<InfoRowGroup> {
recognizer = TapGestureRecognizer()..onTap = () => setState(() => _expandedKeys.add(key));
}
return [TextSpan(text: '${Constants.fsi}$value${Constants.pdi}', recognizer: recognizer)];
return [TextSpan(text: _buildTextValue(value), recognizer: recognizer)];
}
// each text span embeds and pops a Bidi isolate,
// so that layout of the spans follows the directionality of the locale
// (e.g. keys on the right for RTL locale, whatever the key intrinsic directionality)
// and each span respects the directionality of its inner text only
String _buildTextValue(String value) => '${Constants.fsi}$value${Constants.pdi}';
}
typedef InfoValueSpanBuilder = List<InlineSpan> Function(BuildContext context, String key, String value);

View file

@ -50,13 +50,15 @@ class InfoAppBar extends StatelessWidget {
return SliverAppBar(
leading: useTvLayout
? null
: IconButton(
: FontSizeIconTheme(
child: IconButton(
// key is expected by test driver
key: const Key('back-button'),
icon: const Icon(AIcons.goUp),
onPressed: onBackPressed,
tooltip: context.l10n.viewerInfoBackToViewerTooltip,
),
),
automaticallyImplyLeading: false,
title: SliverAppBarTitleWrapper(
child: InteractiveAppBarTitle(
@ -73,8 +75,7 @@ class InfoAppBar extends StatelessWidget {
tooltip: MaterialLocalizations.of(context).searchFieldLabel,
),
if (entry.canEdit)
FontSizeIconTheme(
child: PopupMenuButton<EntryAction>(
PopupMenuButton<EntryAction>(
itemBuilder: (context) => [
...commonActions.map((action) => _toMenuItem(context, action, enabled: actionDelegate.canApply(entry, action))),
if (formatSpecificActions.isNotEmpty) ...[
@ -92,8 +93,7 @@ class InfoAppBar extends StatelessWidget {
actionDelegate.onActionSelected(context, entry, collection, action);
},
),
),
],
].map((v) => FontSizeIconTheme(child: v)).toList(),
floating: true,
);
}