fixed scintillating thumbnail borders & selection overlay layout

This commit is contained in:
Thibault Deckers 2022-01-07 18:27:14 +09:00
parent c8c2537996
commit 14385eeadd
9 changed files with 58 additions and 40 deletions

View file

@ -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(

View file

@ -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);
}

View file

@ -30,8 +30,9 @@ class GridItemSelectionOverlay<T> 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,

View file

@ -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),

View file

@ -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<AvesEntry>(
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,

View file

@ -28,7 +28,7 @@ class ThumbnailEntryOverlay extends StatelessWidget {
const AnimatedImageIcon()
else ...[
if (entry.isRaw && context.select<GridThemeData, bool>((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,

View file

@ -37,7 +37,7 @@ class FilterListDetails<T extends CollectionFilter> extends StatelessWidget {
return Container(
padding: FilterListDetailsTheme.contentPadding,
foregroundDecoration: BoxDecoration(
border: Border(top: AvesBorder.side),
border: Border(top: AvesBorder.straightSide),
),
margin: FilterListDetailsTheme.contentMargin,
child: Column(

View file

@ -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<Color>(Colors.white),
overlayColor: MaterialStateProperty.all<Color>(Colors.white.withOpacity(0.12)),
minimumSize: _minSize,
side: MaterialStateProperty.all<BorderSide>(AvesBorder.side),
side: MaterialStateProperty.all<BorderSide>(AvesBorder.curvedSide),
shape: MaterialStateProperty.all<OutlinedBorder>(const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(_borderRadius)),
)),

View file

@ -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: