From 14385eeadd5f04158479275cf32cdd205545a743 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Fri, 7 Jan 2022 18:27:14 +0900 Subject: [PATCH] fixed scintillating thumbnail borders & selection overlay layout --- lib/widgets/collection/grid/list_details.dart | 2 +- lib/widgets/common/fx/borders.dart | 18 +++++++-- lib/widgets/common/grid/overlay.dart | 3 +- lib/widgets/common/identity/aves_icons.dart | 9 +++-- lib/widgets/common/thumbnail/decorated.dart | 16 +++++--- lib/widgets/common/thumbnail/overlay.dart | 4 +- .../filter_grids/common/list_details.dart | 2 +- lib/widgets/viewer/overlay/common.dart | 4 +- pubspec.lock | 40 +++++++++---------- 9 files changed, 58 insertions(+), 40 deletions(-) diff --git a/lib/widgets/collection/grid/list_details.dart b/lib/widgets/collection/grid/list_details.dart index 149ffe83a..5170b5327 100644 --- a/lib/widgets/collection/grid/list_details.dart +++ b/lib/widgets/collection/grid/list_details.dart @@ -25,7 +25,7 @@ class EntryListDetails extends StatelessWidget { return Container( padding: EntryListDetailsTheme.contentPadding, foregroundDecoration: BoxDecoration( - border: Border(top: AvesBorder.side), + border: Border(top: AvesBorder.straightSide), ), margin: EntryListDetailsTheme.contentMargin, child: IconTheme.merge( diff --git a/lib/widgets/common/fx/borders.dart b/lib/widgets/common/fx/borders.dart index 830cdd4b5..766cd8671 100644 --- a/lib/widgets/common/fx/borders.dart +++ b/lib/widgets/common/fx/borders.dart @@ -6,12 +6,22 @@ class AvesBorder { static const borderColor = Colors.white30; // directly uses `devicePixelRatio` as it never changes, to avoid visiting ancestors via `MediaQuery` - static double get borderWidth => window.devicePixelRatio > 2 ? 0.5 : 1.0; - static BorderSide get side => BorderSide( + // 1 device pixel for straight lines is fine + static double get straightBorderWidth => 1 / window.devicePixelRatio; + + // 1 device pixel for curves is too thin + static double get curvedBorderWidth => window.devicePixelRatio > 2 ? 0.5 : 1.0; + + static BorderSide get straightSide => BorderSide( color: borderColor, - width: borderWidth, + width: straightBorderWidth, ); - static Border get border => Border.fromBorderSide(side); + static BorderSide get curvedSide => BorderSide( + color: borderColor, + width: curvedBorderWidth, + ); + + static Border get border => Border.fromBorderSide(curvedSide); } diff --git a/lib/widgets/common/grid/overlay.dart b/lib/widgets/common/grid/overlay.dart index dafa4fd7d..c9c111df4 100644 --- a/lib/widgets/common/grid/overlay.dart +++ b/lib/widgets/common/grid/overlay.dart @@ -30,8 +30,9 @@ class GridItemSelectionOverlay extends StatelessWidget { ? OverlayIcon( key: ValueKey(isSelected), icon: isSelected ? AIcons.selected : AIcons.unselected, + margin: EdgeInsets.zero, ) - : const SizedBox.shrink(); + : const SizedBox(); child = AnimatedSwitcher( duration: duration, switchInCurve: Curves.easeOutBack, diff --git a/lib/widgets/common/identity/aves_icons.dart b/lib/widgets/common/identity/aves_icons.dart index 4527a50ac..cf6596df2 100644 --- a/lib/widgets/common/identity/aves_icons.dart +++ b/lib/widgets/common/identity/aves_icons.dart @@ -50,8 +50,8 @@ class AnimatedImageIcon extends StatelessWidget { } } -class GeotiffIcon extends StatelessWidget { - const GeotiffIcon({Key? key}) : super(key: key); +class GeoTiffIcon extends StatelessWidget { + const GeoTiffIcon({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -181,12 +181,15 @@ class OverlayIcon extends StatelessWidget { final IconData icon; final String? text; final double iconScale; + final EdgeInsets margin; const OverlayIcon({ Key? key, required this.icon, this.iconScale = 1, this.text, + // default margin for multiple icons in a `Column` + this.margin = const EdgeInsets.only(left: 1, right: 1, bottom: 1), }) : super(key: key); @override @@ -211,7 +214,7 @@ class OverlayIcon extends StatelessWidget { ); return Container( - margin: const EdgeInsets.only(left: 1, right: 1, bottom: 1), + margin: margin, padding: text != null ? EdgeInsets.only(right: size / 4) : null, decoration: BoxDecoration( color: const Color(0xBB000000), diff --git a/lib/widgets/common/thumbnail/decorated.dart b/lib/widgets/common/thumbnail/decorated.dart index ebb815eea..bcf87ee42 100644 --- a/lib/widgets/common/thumbnail/decorated.dart +++ b/lib/widgets/common/thumbnail/decorated.dart @@ -13,7 +13,7 @@ class DecoratedThumbnail extends StatelessWidget { final Object? Function()? heroTagger; static final Color borderColor = Colors.grey.shade700; - static final double borderWidth = AvesBorder.borderWidth; + static final double borderWidth = AvesBorder.straightBorderWidth; const DecoratedThumbnail({ Key? key, @@ -27,12 +27,10 @@ class DecoratedThumbnail extends StatelessWidget { @override Widget build(BuildContext context) { - final imageExtent = tileExtent - borderWidth * 2; - final isSvg = entry.isSvg; Widget child = ThumbnailImage( entry: entry, - extent: imageExtent, + extent: tileExtent, cancellableNotifier: cancellableNotifier, heroTag: heroTagger?.call(), ); @@ -42,13 +40,19 @@ class DecoratedThumbnail extends StatelessWidget { children: [ child, if (!isSvg) ThumbnailEntryOverlay(entry: entry), - if (selectable) GridItemSelectionOverlay(item: entry), + if (selectable) + GridItemSelectionOverlay( + item: entry, + padding: const EdgeInsets.all(2), + ), if (highlightable) ThumbnailHighlightOverlay(entry: entry), ], ); return Container( - decoration: BoxDecoration( + // `decoration` with sub logical pixel width yields scintillating borders + // so we use `foregroundDecoration` instead + foregroundDecoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( color: borderColor, width: borderWidth, diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index 388604013..28b8251b5 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -28,7 +28,7 @@ class ThumbnailEntryOverlay extends StatelessWidget { const AnimatedImageIcon() else ...[ if (entry.isRaw && context.select((t) => t.showRaw)) const RawIcon(), - if (entry.isGeotiff) const GeotiffIcon(), + if (entry.isGeotiff) const GeoTiffIcon(), if (entry.is360) const SphericalImageIcon(), ], if (entry.isMultiPage) ...[ @@ -36,7 +36,7 @@ class ThumbnailEntryOverlay extends StatelessWidget { if (!entry.isMotionPhoto) MultiPageIcon(entry: entry), ], ]; - if (children.isEmpty) return const SizedBox.shrink(); + if (children.isEmpty) return const SizedBox(); if (children.length == 1) return children.first; return Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/widgets/filter_grids/common/list_details.dart b/lib/widgets/filter_grids/common/list_details.dart index 1b3bd9ecc..1db0991e1 100644 --- a/lib/widgets/filter_grids/common/list_details.dart +++ b/lib/widgets/filter_grids/common/list_details.dart @@ -37,7 +37,7 @@ class FilterListDetails extends StatelessWidget { return Container( padding: FilterListDetailsTheme.contentPadding, foregroundDecoration: BoxDecoration( - border: Border(top: AvesBorder.side), + border: Border(top: AvesBorder.straightSide), ), margin: FilterListDetailsTheme.contentMargin, child: Column( diff --git a/lib/widgets/viewer/overlay/common.dart b/lib/widgets/viewer/overlay/common.dart index 044933971..7caf6446d 100644 --- a/lib/widgets/viewer/overlay/common.dart +++ b/lib/widgets/viewer/overlay/common.dart @@ -38,7 +38,7 @@ class OverlayButton extends StatelessWidget { } // icon (24) + icon padding (8) + button padding (16) + border (1 or 2) - static double getSize(BuildContext context) => 48.0 + AvesBorder.borderWidth * 2; + static double getSize(BuildContext context) => 48.0 + AvesBorder.curvedBorderWidth * 2; } class OverlayTextButton extends StatelessWidget { @@ -71,7 +71,7 @@ class OverlayTextButton extends StatelessWidget { foregroundColor: MaterialStateProperty.all(Colors.white), overlayColor: MaterialStateProperty.all(Colors.white.withOpacity(0.12)), minimumSize: _minSize, - side: MaterialStateProperty.all(AvesBorder.side), + side: MaterialStateProperty.all(AvesBorder.curvedSide), shape: MaterialStateProperty.all(const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(_borderRadius)), )), diff --git a/pubspec.lock b/pubspec.lock index c65a3bfd8..79c46432a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -119,14 +119,14 @@ packages: name: connectivity_plus url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" connectivity_plus_linux: dependency: transitive description: name: connectivity_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.2.0" connectivity_plus_macos: dependency: transitive description: @@ -140,14 +140,14 @@ packages: name: connectivity_plus_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.2.0" connectivity_plus_web: dependency: transitive description: name: connectivity_plus_web url: "https://pub.dartlang.org" source: hosted - version: "1.1.0+1" + version: "1.2.0" connectivity_plus_windows: dependency: transitive description: @@ -210,21 +210,21 @@ packages: name: device_info_plus url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "3.2.1" device_info_plus_linux: dependency: transitive description: name: device_info_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" device_info_plus_macos: dependency: transitive description: name: device_info_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.1" device_info_plus_platform_interface: dependency: transitive description: @@ -245,7 +245,7 @@ packages: name: device_info_plus_windows url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" equatable: dependency: "direct main" description: @@ -447,7 +447,7 @@ packages: name: github url: "https://pub.dartlang.org" source: hosted - version: "8.3.0" + version: "8.5.0" glob: dependency: transitive description: @@ -475,7 +475,7 @@ packages: name: google_maps_flutter_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" highlight: dependency: transitive description: @@ -734,7 +734,7 @@ packages: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" path_provider_windows: dependency: transitive description: @@ -797,7 +797,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.1.0" pool: dependency: transitive description: @@ -839,7 +839,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "6.0.1" + version: "6.0.2" pub_semver: dependency: transitive description: @@ -867,28 +867,28 @@ packages: name: screen_brightness url: "https://pub.dartlang.org" source: hosted - version: "0.1.2+2" + version: "0.1.3" screen_brightness_android: dependency: transitive description: name: screen_brightness_android url: "https://pub.dartlang.org" source: hosted - version: "0.0.1" + version: "0.0.3" screen_brightness_ios: dependency: transitive description: name: screen_brightness_ios url: "https://pub.dartlang.org" source: hosted - version: "0.0.2" + version: "0.0.4" screen_brightness_platform_interface: dependency: transitive description: name: screen_brightness_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.0.2" + version: "0.0.3" shared_preferences: dependency: "direct main" description: @@ -1012,7 +1012,7 @@ packages: name: sqflite_common url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.2.0" stack_trace: dependency: transitive description: @@ -1161,7 +1161,7 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" url_launcher_web: dependency: transitive description: @@ -1231,7 +1231,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.1" + version: "2.3.3" wkt_parser: dependency: transitive description: