From 810f32d5422799138112e24cb132b0413fb12d62 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 24 May 2020 10:21:43 +0900 Subject: [PATCH] apply text scale factor --- lib/labs/outlined_text.dart | 8 +- lib/utils/android_file_utils.dart | 2 +- lib/widgets/about/about_page.dart | 4 +- lib/widgets/about/licenses.dart | 9 ++- lib/widgets/album/grid/header_generic.dart | 12 +-- .../album/grid/list_section_layout.dart | 2 +- lib/widgets/app_drawer.dart | 26 +++--- lib/widgets/common/menu_row.dart | 2 +- lib/widgets/stats/filter_table.dart | 7 +- lib/widgets/stats/stats.dart | 81 ++++++++++--------- 10 files changed, 85 insertions(+), 68 deletions(-) diff --git a/lib/labs/outlined_text.dart b/lib/labs/outlined_text.dart index 1c232d0eb..088d2a229 100644 --- a/lib/labs/outlined_text.dart +++ b/lib/labs/outlined_text.dart @@ -27,8 +27,8 @@ class OutlinedText extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - RichText( - text: TextSpan( + Text.rich( + TextSpan( children: [ if (leadingBuilder != null) WidgetSpan( @@ -52,8 +52,8 @@ class OutlinedText extends StatelessWidget { ], ), ), - RichText( - text: TextSpan( + Text.rich( + TextSpan( children: [ if (leadingBuilder != null) WidgetSpan( diff --git a/lib/utils/android_file_utils.dart b/lib/utils/android_file_utils.dart index ceee8c493..c5354dac2 100644 --- a/lib/utils/android_file_utils.dart +++ b/lib/utils/android_file_utils.dart @@ -34,7 +34,7 @@ class AndroidFileUtils { StorageVolume getStorageVolume(String path) => storageVolumes.firstWhere((v) => path.startsWith(v.path), orElse: () => null); - bool isOnRemovableStorage(String path) => getStorageVolume(path).isRemovable; + bool isOnRemovableStorage(String path) => getStorageVolume(path)?.isRemovable ?? false; AlbumType getAlbumType(String albumDirectory) { if (albumDirectory != null) { diff --git a/lib/widgets/about/about_page.dart b/lib/widgets/about/about_page.dart index 44746d147..cc8b4c989 100644 --- a/lib/widgets/about/about_page.dart +++ b/lib/widgets/about/about_page.dart @@ -17,8 +17,8 @@ class AboutPage extends StatelessWidget { delegate: SliverChildListDelegate( [ Center( - child: RichText( - text: TextSpan( + child: Text.rich( + TextSpan( children: [ const TextSpan(text: 'Made with ❤️ and '), WidgetSpan( diff --git a/lib/widgets/about/licenses.dart b/lib/widgets/about/licenses.dart index a8b1482d1..2462c122a 100644 --- a/lib/widgets/about/licenses.dart +++ b/lib/widgets/about/licenses.dart @@ -59,11 +59,12 @@ class _LicensesState extends State { padding: const EdgeInsetsDirectional.only(start: 8), child: Row( children: [ - Text( - 'Open-source licenses', - style: Theme.of(context).textTheme.headline6.copyWith(fontFamily: 'Concourse Caps'), + Expanded( + child: Text( + 'Open-Source Licenses', + style: Theme.of(context).textTheme.headline6.copyWith(fontFamily: 'Concourse Caps'), + ), ), - const Spacer(), PopupMenuButton( itemBuilder: (context) => [ PopupMenuItem( diff --git a/lib/widgets/album/grid/header_generic.dart b/lib/widgets/album/grid/header_generic.dart index e876554f3..3b76accbc 100644 --- a/lib/widgets/album/grid/header_generic.dart +++ b/lib/widgets/album/grid/header_generic.dart @@ -67,8 +67,9 @@ class SectionHeader extends StatelessWidget { } // TODO TLAD cache header extent computation? - static double computeHeaderHeight(CollectionSource source, dynamic sectionKey, double scrollableWidth) { + static double computeHeaderHeight(BuildContext context, CollectionSource source, dynamic sectionKey, double scrollableWidth) { var headerExtent = 0.0; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); if (sectionKey is String) { // only compute height for album headers, as they're the only likely ones to split on multiple lines final hasLeading = androidFileUtils.getAlbumType(sectionKey) != AlbumType.regular; @@ -83,7 +84,7 @@ class SectionHeader extends StatelessWidget { TextSpan( text: '\u200A' * (hasLeading ? 23 : 1), // force a higher first line to match leading icon/selector dimension - style: const TextStyle(height: 2.3), + style: TextStyle(height: 2.3 * textScaleFactor), ), // 23 hair spaces match a width of 40.0 if (hasTrailing) TextSpan(text: '\u200A' * 17), @@ -94,10 +95,11 @@ class SectionHeader extends StatelessWidget { ], ), textDirection: TextDirection.ltr, + textScaleFactor: textScaleFactor, )..layout(BoxConstraints(maxWidth: maxWidth), parentUsesSize: true); headerExtent = para.getMaxIntrinsicHeight(maxWidth); } - headerExtent = max(headerExtent, TitleSectionHeader.leadingDimension) + TitleSectionHeader.padding.vertical; + headerExtent = max(headerExtent, TitleSectionHeader.leadingDimension * textScaleFactor) + TitleSectionHeader.padding.vertical; return headerExtent; } } @@ -127,8 +129,8 @@ class TitleSectionHeader extends StatelessWidget { alignment: AlignmentDirectional.centerStart, padding: padding, constraints: const BoxConstraints(minHeight: leadingDimension), - child: RichText( - text: TextSpan( + child: Text.rich( + TextSpan( children: [ WidgetSpan( alignment: widgetSpanAlignment, diff --git a/lib/widgets/album/grid/list_section_layout.dart b/lib/widgets/album/grid/list_section_layout.dart index fc3da168d..f98da68b1 100644 --- a/lib/widgets/album/grid/list_section_layout.dart +++ b/lib/widgets/album/grid/list_section_layout.dart @@ -44,7 +44,7 @@ class SectionedListLayoutProvider extends StatelessWidget { final sectionEntryCount = sections[sectionKey].length; final sectionChildCount = 1 + (sectionEntryCount / columnCount).ceil(); - final headerExtent = showHeaders ? SectionHeader.computeHeaderHeight(source, sectionKey, scrollableWidth) : 0.0; + final headerExtent = showHeaders ? SectionHeader.computeHeaderHeight(context, source, sectionKey, scrollableWidth) : 0.0; final sectionFirstIndex = currentIndex; currentIndex += sectionChildCount; diff --git a/lib/widgets/app_drawer.dart b/lib/widgets/app_drawer.dart index a5d5d8915..163ae2682 100644 --- a/lib/widgets/app_drawer.dart +++ b/lib/widgets/app_drawer.dart @@ -50,18 +50,22 @@ class _AppDrawerState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - const AvesLogo(size: 64), - const SizedBox(width: 16), - const Text( - 'Aves', - style: TextStyle( - fontSize: 44, - fontFamily: 'Concourse Caps', + Align( + alignment: AlignmentDirectional.centerStart, + child: Wrap( + spacing: 16, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const AvesLogo(size: 64), + const Text( + 'Aves', + style: TextStyle( + fontSize: 44, + fontFamily: 'Concourse Caps', + ), ), - ), - ], + ], + ), ), ], ), diff --git a/lib/widgets/common/menu_row.dart b/lib/widgets/common/menu_row.dart index dbb03d78a..ce73e31d8 100644 --- a/lib/widgets/common/menu_row.dart +++ b/lib/widgets/common/menu_row.dart @@ -28,7 +28,7 @@ class MenuRow extends StatelessWidget { Icon(icon), const SizedBox(width: 8), ], - Text(text), + Expanded(child: Text(text)), ], ); } diff --git a/lib/widgets/stats/filter_table.dart b/lib/widgets/stats/filter_table.dart index 4eb9a60e3..af99dd800 100644 --- a/lib/widgets/stats/filter_table.dart +++ b/lib/widgets/stats/filter_table.dart @@ -32,6 +32,9 @@ class FilterTable extends StatelessWidget { return c != 0 ? c : compareAsciiUpperCase(kv1.key, kv2.key); }); + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final lineHeight = 16 * textScaleFactor; + return Padding( padding: const EdgeInsetsDirectional.only(start: AvesFilterChip.buttonBorderWidth / 2 + 6, end: 8), child: LayoutBuilder( @@ -55,11 +58,11 @@ class FilterTable extends StatelessWidget { if (showPercentIndicator) LinearPercentIndicator( percent: percent, - lineHeight: 16, + lineHeight: lineHeight, backgroundColor: Colors.white24, progressColor: stringToColor(label), animation: true, - padding: const EdgeInsets.symmetric(horizontal: 16), + padding: EdgeInsets.symmetric(horizontal: lineHeight), center: Text(NumberFormat.percentPattern().format(percent)), ), Text( diff --git a/lib/widgets/stats/stats.dart b/lib/widgets/stats/stats.dart index e2098d08d..5ac88639a 100644 --- a/lib/widgets/stats/stats.dart +++ b/lib/widgets/stats/stats.dart @@ -51,41 +51,46 @@ class StatsPage extends StatelessWidget { if (collection.isEmpty) { child = const EmptyContent(); } else { - final catalogued = entries.where((entry) => entry.isCatalogued); - final withGps = catalogued.where((entry) => entry.hasGps); - final withGpsPercent = withGps.length / collection.entryCount; final byMimeTypes = groupBy(entries, (entry) => entry.mimeType).map((k, v) => MapEntry(k, v.length)); final imagesByMimeTypes = Map.fromEntries(byMimeTypes.entries.where((kv) => kv.key.startsWith('image/'))); final videoByMimeTypes = Map.fromEntries(byMimeTypes.entries.where((kv) => kv.key.startsWith('video/'))); + final mimeDonuts = Wrap( + alignment: WrapAlignment.center, + children: [ + _buildMimeDonut(context, (sum) => Intl.plural(sum, one: 'image', other: 'images'), imagesByMimeTypes), + _buildMimeDonut(context, (sum) => Intl.plural(sum, one: 'video', other: 'videos'), videoByMimeTypes), + ], + ); + + final catalogued = entries.where((entry) => entry.isCatalogued); + final withGps = catalogued.where((entry) => entry.hasGps); + final withGpsPercent = withGps.length / collection.entryCount; + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final lineHeight = 16 * textScaleFactor; + final locationIndicator = Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + LinearPercentIndicator( + percent: withGpsPercent, + lineHeight: lineHeight, + backgroundColor: Colors.white24, + progressColor: Theme.of(context).accentColor, + animation: true, + leading: const Icon(AIcons.location), + // right padding to match leading, so that inside label is aligned with outside label below + padding: EdgeInsets.symmetric(horizontal: lineHeight) + const EdgeInsets.only(right: 24), + center: Text(NumberFormat.percentPattern().format(withGpsPercent)), + ), + const SizedBox(height: 8), + Text('${withGps.length} ${Intl.plural(withGps.length, one: 'item', other: 'items')} with location'), + ], + ), + ); child = ListView( children: [ - Wrap( - alignment: WrapAlignment.center, - children: [ - _buildMimeDonut(context, (sum) => Intl.plural(sum, one: 'image', other: 'images'), imagesByMimeTypes), - _buildMimeDonut(context, (sum) => Intl.plural(sum, one: 'video', other: 'videos'), videoByMimeTypes), - ], - ), - Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - LinearPercentIndicator( - percent: withGpsPercent, - lineHeight: 16, - backgroundColor: Colors.white24, - progressColor: Theme.of(context).accentColor, - animation: true, - leading: const Icon(AIcons.location), - // right padding to match leading, so that inside label is aligned with outside label below - padding: const EdgeInsets.symmetric(horizontal: 16) + const EdgeInsets.only(right: 24), - center: Text(NumberFormat.percentPattern().format(withGpsPercent)), - ), - const SizedBox(height: 8), - Text('${withGps.length} ${Intl.plural(withGps.length, one: 'item', other: 'items')} with location'), - ], - ), - ), + mimeDonuts, + locationIndicator, ..._buildTopFilters('Top Countries', entryCountPerCountry, (s) => LocationFilter(LocationLevel.country, s)), ..._buildTopFilters('Top Places', entryCountPerPlace, (s) => LocationFilter(LocationLevel.place, s)), ..._buildTopFilters('Top Tags', entryCountPerTag, (s) => TagFilter(s)), @@ -132,8 +137,10 @@ class StatsPage extends StatelessWidget { ]; return LayoutBuilder(builder: (context, constraints) { + final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final minWidth = mimeDonutMinWidth * textScaleFactor; final availableWidth = constraints.maxWidth; - final dim = max(mimeDonutMinWidth, availableWidth / (availableWidth > 4 * mimeDonutMinWidth ? 4 : 2)); + final dim = max(minWidth, availableWidth / (availableWidth > 4 * minWidth ? 4 : (availableWidth > 2 * minWidth ? 2 : 1))); final donut = Container( width: dim, @@ -161,11 +168,8 @@ class StatsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: seriesData - .map((kv) => RichText( - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 1, - text: TextSpan( + .map((kv) => Text.rich( + TextSpan( children: [ WidgetSpan( alignment: PlaceholderAlignment.middle, @@ -178,6 +182,9 @@ class StatsPage extends StatelessWidget { TextSpan(text: '${kv.value}', style: const TextStyle(color: Colors.white70)), ], ), + overflow: TextOverflow.fade, + softWrap: false, + maxLines: 1, )) .toList(), ), @@ -186,7 +193,7 @@ class StatsPage extends StatelessWidget { donut, legend, ]; - return availableWidth > mimeDonutMinWidth * 2 + return availableWidth > minWidth * 2 ? Row( mainAxisSize: MainAxisSize.min, children: children,