fixed scintillating thumbnail borders & selection overlay layout
This commit is contained in:
parent
c8c2537996
commit
14385eeadd
9 changed files with 58 additions and 40 deletions
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)),
|
||||
)),
|
||||
|
|
40
pubspec.lock
40
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:
|
||||
|
|
Loading…
Reference in a new issue