diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index bc396e55e..56acb0141 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -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 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,66 +381,62 @@ class _CollectionAppBarState extends State 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( - // key is expected by test driver - key: const Key('appbar-menu-button'), - itemBuilder: (context) { - final generalMenuItems = EntrySetActions.general.where(isVisible).map( - (action) => _toMenuItem(action, enabled: canApply(action), selection: selection), - ); + PopupMenuButton( + // key is expected by test driver + key: const Key('appbar-menu-button'), + itemBuilder: (context) { + final generalMenuItems = EntrySetActions.general.where(isVisible).map( + (action) => _toMenuItem(action, enabled: canApply(action), selection: selection), + ); - final browsingMenuActions = EntrySetActions.pageBrowsing.where((v) => !browsingQuickActions.contains(v)); - final selectionMenuActions = EntrySetActions.pageSelection.where((v) => !selectionQuickActions.contains(v)); - final contextualMenuActions = (isSelecting ? selectionMenuActions : browsingMenuActions).where((v) => v == null || isVisible(v)).fold([], (prev, v) { - if (v == null && (prev.isEmpty || prev.last == null)) return prev; - return [...prev, v]; - }); - if (contextualMenuActions.isNotEmpty && contextualMenuActions.last == null) { - contextualMenuActions.removeLast(); - } + final browsingMenuActions = EntrySetActions.pageBrowsing.where((v) => !browsingQuickActions.contains(v)); + final selectionMenuActions = EntrySetActions.pageSelection.where((v) => !selectionQuickActions.contains(v)); + final contextualMenuActions = (isSelecting ? selectionMenuActions : browsingMenuActions).where((v) => v == null || isVisible(v)).fold([], (prev, v) { + if (v == null && (prev.isEmpty || prev.last == null)) return prev; + return [...prev, v]; + }); + if (contextualMenuActions.isNotEmpty && contextualMenuActions.last == null) { + contextualMenuActions.removeLast(); + } - final contextualMenuItems = >[ - ...contextualMenuActions.map( - (action) { - if (action == null) return const PopupMenuDivider(); - return _toMenuItem(action, enabled: canApply(action), selection: selection); - }, + final contextualMenuItems = >[ + ...contextualMenuActions.map( + (action) { + if (action == null) return const PopupMenuDivider(); + return _toMenuItem(action, enabled: canApply(action), selection: selection); + }, + ), + if (isSelecting && !settings.isReadOnly && appMode == AppMode.main && !isTrash) + PopupMenuExpansionPanel( + enabled: hasSelection, + value: 'edit', + icon: AIcons.edit, + title: context.l10n.collectionActionEdit, + items: [ + _buildRotateAndFlipMenuItems(context, canApply: canApply), + ...EntrySetActions.edit.where((v) => isVisible(v) && !selectionQuickActions.contains(v)).map((action) => _toMenuItem(action, enabled: canApply(action), selection: selection)), + ], ), - if (isSelecting && !settings.isReadOnly && appMode == AppMode.main && !isTrash) - PopupMenuExpansionPanel( - enabled: hasSelection, - value: 'edit', - icon: AIcons.edit, - title: context.l10n.collectionActionEdit, - items: [ - _buildRotateAndFlipMenuItems(context, canApply: canApply), - ...EntrySetActions.edit.where((v) => isVisible(v) && !selectionQuickActions.contains(v)).map((action) => _toMenuItem(action, enabled: canApply(action), selection: selection)), - ], - ), - ]; + ]; - return [ - ...generalMenuItems, - if (contextualMenuItems.isNotEmpty) ...[ - const PopupMenuDivider(), - ...contextualMenuItems, - ], - ]; - }, - onSelected: (action) async { - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(Durations.popupMenuAnimation * timeDilation); - await _onActionSelected(action); - }, - ), + return [ + ...generalMenuItems, + if (contextualMenuItems.isNotEmpty) ...[ + const PopupMenuDivider(), + ...contextualMenuItems, + ], + ]; + }, + onSelected: (action) async { + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + await _onActionSelected(action); + }, ), ]; } diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index f1d78f0b5..eb9e1c88a 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -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( 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, diff --git a/lib/widgets/common/basic/draggable_scrollbar/scrollbar.dart b/lib/widgets/common/basic/draggable_scrollbar/scrollbar.dart index ee6fdb0f2..9e4bf83c9 100644 --- a/lib/widgets/common/basic/draggable_scrollbar/scrollbar.dart +++ b/lib/widgets/common/basic/draggable_scrollbar/scrollbar.dart @@ -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 createState() => _DraggableScrollbarState(); diff --git a/lib/widgets/common/basic/wheel.dart b/lib/widgets/common/basic/wheel.dart index 0434a3c12..b6f190380 100644 --- a/lib/widgets/common/basic/wheel.dart +++ b/lib/widgets/common/basic/wheel.dart @@ -25,8 +25,6 @@ class _WheelSelectorState extends State> { late final FixedExtentScrollController _controller; final ValueNotifier _focusedNotifier = ValueNotifier(false); - static const itemSize = Size(40, 40); - ValueNotifier get valueNotifier => widget.valueNotifier; List get values => widget.values; @@ -51,6 +49,7 @@ class _WheelSelectorState extends State> { const background = Colors.transparent; final foreground = DefaultTextStyle.of(context).style.color!; final transitionDuration = context.select((v) => v.formTransition); + final itemSize = Size.square(40 * context.select((mq) => mq.textScaleFactor)); return FocusableActionDetector( shortcuts: const { diff --git a/lib/widgets/common/grid/header.dart b/lib/widgets/common/grid/header.dart index d6da38773..fc50da0c3 100644 --- a/lib/widgets/common/grid/header.dart +++ b/lib/widgets/common/grid/header.dart @@ -130,7 +130,7 @@ class SectionHeader 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 diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index 8e787f222..3d5b57559 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -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,7 +66,9 @@ class AvesAppBar extends StatelessWidget { tag: leadingHeroTag, flightShuttleBuilder: _flightShuttleBuilder, transitionOnUserGestures: true, - child: leading!, + child: FontSizeIconTheme( + child: leading!, + ), ), ) : const SizedBox(width: 16), @@ -78,12 +81,14 @@ class AvesAppBar extends StatelessWidget { transitionOnUserGestures: true, child: AnimatedSwitcher( duration: context.read().iconAnimation, - child: Row( - key: ValueKey(transitionKey), - children: [ - Expanded(child: title), - ...actions, - ], + child: FontSizeIconTheme( + child: Row( + key: ValueKey(transitionKey), + children: [ + Expanded(child: title), + ...actions, + ], + ), ), ), ), diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 044a73124..d951da8bc 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -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,32 +209,28 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { return [ if (widget.moveType != null) ..._quickActions.where(isVisible).map( - (action) => FontSizeIconTheme( - child: IconButton( - icon: action.getIcon(), - onPressed: () => onActionSelected(action), - tooltip: action.getText(context), - ), + (action) => IconButton( + icon: action.getIcon(), + onPressed: () => onActionSelected(action), + tooltip: action.getText(context), ), ), - FontSizeIconTheme( - child: PopupMenuButton( - itemBuilder: (context) { - return _menuActions.where((v) => v == null || isVisible(v)).map((action) { - if (action == null) return const PopupMenuDivider(); - return FilterGridAppBar.toMenuItem(context, action, enabled: true); - }).toList(); - }, - onSelected: (action) async { - // remove focus, if any, to prevent the keyboard from showing up - // after the user is done with the popup menu - FocusManager.instance.primaryFocus?.unfocus(); + PopupMenuButton( + itemBuilder: (context) { + return _menuActions.where((v) => v == null || isVisible(v)).map((action) { + if (action == null) return const PopupMenuDivider(); + return FilterGridAppBar.toMenuItem(context, action, enabled: true); + }).toList(); + }, + onSelected: (action) async { + // remove focus, if any, to prevent the keyboard from showing up + // after the user is done with the popup menu + FocusManager.instance.primaryFocus?.unfocus(); - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(Durations.popupMenuAnimation * timeDilation); - onActionSelected(action); - }, - ), + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + onActionSelected(action); + }, ), ]; } diff --git a/lib/widgets/dialogs/tile_view_dialog.dart b/lib/widgets/dialogs/tile_view_dialog.dart index 009499b59..b4aa1d56e 100644 --- a/lib/widgets/dialogs/tile_view_dialog.dart +++ b/lib/widgets/dialogs/tile_view_dialog.dart @@ -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,18 +169,20 @@ class _TileViewDialogState extends State> with final label = ConstrainedBox( constraints: const BoxConstraints(minHeight: kMinInteractiveDimension), - child: Row( - children: [ - Icon(icon), - const SizedBox(width: 16), - Expanded( - child: HighlightTitle( - title: title, - showHighlight: false, + child: FontSizeIconTheme( + child: Row( + children: [ + Icon(icon), + const SizedBox(width: 16), + Expanded( + child: HighlightTitle( + title: title, + showHighlight: false, + ), ), - ), - if (trailing != null) trailing, - ], + if (trailing != null) trailing, + ], + ), ), ); final selector = TextDropdownButton( diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index 729102408..c4061299b 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -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> extends State> with SingleTickerProviderStateMixin { +class _FilterGridAppBarState> extends State> with SingleTickerProviderStateMixin, WidgetsBindingObserver { final List _subscriptions = []; late AnimationController _browseToSelectAnimation; final ValueNotifier _isSelectingNotifier = ValueNotifier(false); @@ -105,6 +104,7 @@ class _FilterGridAppBarState _updateAppBarHeight()); } @@ -118,9 +118,16 @@ class _FilterGridAppBarState 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>().value; @@ -320,53 +327,49 @@ class _FilterGridAppBarState FontSizeIconTheme( - child: _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)), - ), + (action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)), ); return [ ...quickActionButtons, - FontSizeIconTheme( - child: PopupMenuButton( - itemBuilder: (context) { - final generalMenuItems = ChipSetActions.general.where(isVisible).map( - (action) => FilterGridAppBar.toMenuItem(context, action, enabled: canApply(action)), - ); + PopupMenuButton( + itemBuilder: (context) { + final generalMenuItems = ChipSetActions.general.where(isVisible).map( + (action) => FilterGridAppBar.toMenuItem(context, action, enabled: canApply(action)), + ); - final browsingMenuActions = ChipSetActions.browsing.where((v) => !browsingQuickActions.contains(v)); - final selectionMenuActions = ChipSetActions.selection.where((v) => !selectionQuickActions.contains(v)); - final contextualMenuActions = (isSelecting ? selectionMenuActions : browsingMenuActions).where((v) => v == null || isVisible(v)).fold([], (prev, v) { - if (v == null && (prev.isEmpty || prev.last == null)) return prev; - return [...prev, v]; - }); - if (contextualMenuActions.isNotEmpty && contextualMenuActions.last == null) { - contextualMenuActions.removeLast(); - } + final browsingMenuActions = ChipSetActions.browsing.where((v) => !browsingQuickActions.contains(v)); + final selectionMenuActions = ChipSetActions.selection.where((v) => !selectionQuickActions.contains(v)); + final contextualMenuActions = (isSelecting ? selectionMenuActions : browsingMenuActions).where((v) => v == null || isVisible(v)).fold([], (prev, v) { + if (v == null && (prev.isEmpty || prev.last == null)) return prev; + return [...prev, v]; + }); + if (contextualMenuActions.isNotEmpty && contextualMenuActions.last == null) { + contextualMenuActions.removeLast(); + } - return [ - ...generalMenuItems, - if (contextualMenuActions.isNotEmpty) ...[ - const PopupMenuDivider(), - ...contextualMenuActions.map( - (action) { - if (action == null) return const PopupMenuDivider(); - return FilterGridAppBar.toMenuItem(context, action, enabled: canApply(action)); - }, - ), - ], - ]; - }, - onSelected: (action) async { - // remove focus, if any, to prevent the keyboard from showing up - // after the user is done with the popup menu - FocusManager.instance.primaryFocus?.unfocus(); + return [ + ...generalMenuItems, + if (contextualMenuActions.isNotEmpty) ...[ + const PopupMenuDivider(), + ...contextualMenuActions.map( + (action) { + if (action == null) return const PopupMenuDivider(); + return FilterGridAppBar.toMenuItem(context, action, enabled: canApply(action)); + }, + ), + ], + ]; + }, + onSelected: (action) async { + // remove focus, if any, to prevent the keyboard from showing up + // after the user is done with the popup menu + FocusManager.instance.primaryFocus?.unfocus(); - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(Durations.popupMenuAnimation * timeDilation); - _onActionSelected(context, action, actionDelegate); - }, - ), + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + _onActionSelected(context, action, actionDelegate); + }, ), ]; } diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index 26d2c0b65..10ef720f2 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -485,9 +485,31 @@ class _FilterSectionedContentState extends State<_Fi @override void initState() { super.initState(); + _registerWidget(widget); WidgetsBinding.instance.addPostFrameCallback((_) => _checkInitHighlight()); } + @override + void didUpdateWidget(covariant _FilterSectionedContent oldWidget) { + super.didUpdateWidget(oldWidget); + _unregisterWidget(oldWidget); + _registerWidget(widget); + } + + @override + void dispose() { + _unregisterWidget(widget); + super.dispose(); + } + + void _registerWidget(_FilterSectionedContent widget) { + widget.appBarHeightNotifier.addListener(_onAppBarHeightChanged); + } + + void _unregisterWidget(_FilterSectionedContent widget) { + widget.appBarHeightNotifier.removeListener(_onAppBarHeightChanged); + } + @override Widget build(BuildContext context) { final scrollView = AnimationLimiter( @@ -527,6 +549,8 @@ class _FilterSectionedContentState extends State<_Fi ); } + void _onAppBarHeightChanged() => setState(() {}); + Future _checkInitHighlight() async { final highlightInfo = context.read(); final filter = highlightInfo.clear(); @@ -631,7 +655,7 @@ class _FilterScrollView extends StatelessWidget { return settings.useTvLayout ? scrollView : _buildDraggableScrollView(scrollView); } - Widget _buildDraggableScrollView(ScrollView scrollView) { + Widget _buildDraggableScrollView(Widget scrollView) { return ValueListenableBuilder( valueListenable: appBarHeightNotifier, builder: (context, appBarHeight, child) { @@ -672,7 +696,7 @@ class _FilterScrollView extends StatelessWidget { ); } - ScrollView _buildScrollView(BuildContext context) { + Widget _buildScrollView(BuildContext context) { return CustomScrollView( key: scrollableKey, controller: scrollController, diff --git a/lib/widgets/map/map_info_row.dart b/lib/widgets/map/map_info_row.dart index 67a600cbf..4939002b7 100644 --- a/lib/widgets/map/map_info_row.dart +++ b/lib/widgets/map/map_info_row.dart @@ -16,7 +16,6 @@ class MapInfoRow extends StatelessWidget { final ValueNotifier 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((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, diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 120e2e55d..91241b9a2 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -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 extends State with FeedbackMixin { onPressed: () => _goToSearch(context), tooltip: MaterialLocalizations.of(context).searchFieldLabel, ), - FontSizeIconTheme( - child: PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - value: SettingsAction.export, - child: MenuRow(text: context.l10n.settingsActionExport, icon: const Icon(AIcons.fileExport)), - ), - PopupMenuItem( - value: SettingsAction.import, - child: MenuRow(text: context.l10n.settingsActionImport, icon: const Icon(AIcons.fileImport)), - ), - ]; - }, - onSelected: (action) async { - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(Durations.popupMenuAnimation * timeDilation); - _onActionSelected(action); - }, - ), + PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + value: SettingsAction.export, + child: MenuRow(text: context.l10n.settingsActionExport, icon: const Icon(AIcons.fileExport)), + ), + PopupMenuItem( + value: SettingsAction.import, + child: MenuRow(text: context.l10n.settingsActionImport, icon: const Icon(AIcons.fileImport)), + ), + ]; + }, + onSelected: (action) async { + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + _onActionSelected(action); + }, ), - ], + ].map((v) => FontSizeIconTheme(child: v)).toList(), ), body: GestureAreaProtectorStack( child: SafeArea( diff --git a/lib/widgets/viewer/info/common.dart b/lib/widgets/viewer/info/common.dart index 8f73ced5e..a770b5625 100644 --- a/lib/widgets/viewer/info/common.dart +++ b/lib/widgets/viewer/info/common.dart @@ -97,7 +97,7 @@ class _InfoRowGroupState extends State { // 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 { 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 { 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 Function(BuildContext context, String key, String value); diff --git a/lib/widgets/viewer/info/info_app_bar.dart b/lib/widgets/viewer/info/info_app_bar.dart index d2a6525d7..a54eded5e 100644 --- a/lib/widgets/viewer/info/info_app_bar.dart +++ b/lib/widgets/viewer/info/info_app_bar.dart @@ -50,12 +50,14 @@ class InfoAppBar extends StatelessWidget { return SliverAppBar( leading: useTvLayout ? null - : IconButton( - // key is expected by test driver - key: const Key('back-button'), - icon: const Icon(AIcons.goUp), - onPressed: onBackPressed, - tooltip: context.l10n.viewerInfoBackToViewerTooltip, + : 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( @@ -73,27 +75,25 @@ class InfoAppBar extends StatelessWidget { tooltip: MaterialLocalizations.of(context).searchFieldLabel, ), if (entry.canEdit) - FontSizeIconTheme( - child: PopupMenuButton( - itemBuilder: (context) => [ - ...commonActions.map((action) => _toMenuItem(context, action, enabled: actionDelegate.canApply(entry, action))), - if (formatSpecificActions.isNotEmpty) ...[ - const PopupMenuDivider(), - ...formatSpecificActions.map((action) => _toMenuItem(context, action, enabled: actionDelegate.canApply(entry, action))), - ], - if (!kReleaseMode) ...[ - const PopupMenuDivider(), - _toMenuItem(context, EntryAction.debug, enabled: true), - ] + PopupMenuButton( + itemBuilder: (context) => [ + ...commonActions.map((action) => _toMenuItem(context, action, enabled: actionDelegate.canApply(entry, action))), + if (formatSpecificActions.isNotEmpty) ...[ + const PopupMenuDivider(), + ...formatSpecificActions.map((action) => _toMenuItem(context, action, enabled: actionDelegate.canApply(entry, action))), ], - onSelected: (action) async { - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(Durations.popupMenuAnimation * timeDilation); - actionDelegate.onActionSelected(context, entry, collection, action); - }, - ), + if (!kReleaseMode) ...[ + const PopupMenuDivider(), + _toMenuItem(context, EntryAction.debug, enabled: true), + ] + ], + onSelected: (action) async { + // wait for the popup menu to hide before proceeding with the action + await Future.delayed(Durations.popupMenuAnimation * timeDilation); + actionDelegate.onActionSelected(context, entry, collection, action); + }, ), - ], + ].map((v) => FontSizeIconTheme(child: v)).toList(), floating: true, ); }