diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e3eaaa46e..2a1adc515 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -622,6 +622,7 @@ "settingsThumbnailOverlayTile": "Overlay", "settingsThumbnailOverlayTitle": "Overlay", "settingsThumbnailShowFavouriteIcon": "Show favorite icon", + "settingsThumbnailShowTagIcon": "Show tag icon", "settingsThumbnailShowLocationIcon": "Show location icon", "settingsThumbnailShowMotionPhotoIcon": "Show motion photo icon", "settingsThumbnailShowRating": "Show rating", diff --git a/lib/model/settings/defaults.dart b/lib/model/settings/defaults.dart index cbe34b9d1..5b5b86338 100644 --- a/lib/model/settings/defaults.dart +++ b/lib/model/settings/defaults.dart @@ -54,6 +54,7 @@ class SettingsDefaults { EntrySetAction.delete, ]; static const showThumbnailFavourite = true; + static const showThumbnailTag = false; static const showThumbnailLocation = true; static const showThumbnailMotionPhoto = true; static const showThumbnailRating = true; diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index e7b25794e..4188a5829 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -71,6 +71,7 @@ class Settings extends ChangeNotifier { static const collectionBrowsingQuickActionsKey = 'collection_browsing_quick_actions'; static const collectionSelectionQuickActionsKey = 'collection_selection_quick_actions'; static const showThumbnailFavouriteKey = 'show_thumbnail_favourite'; + static const showThumbnailTagKey = 'show_thumbnail_tag'; static const showThumbnailLocationKey = 'show_thumbnail_location'; static const showThumbnailMotionPhotoKey = 'show_thumbnail_motion_photo'; static const showThumbnailRatingKey = 'show_thumbnail_rating'; @@ -354,6 +355,10 @@ class Settings extends ChangeNotifier { set showThumbnailFavourite(bool newValue) => setAndNotify(showThumbnailFavouriteKey, newValue); + bool get showThumbnailTag => getBoolOrDefault(showThumbnailTagKey, SettingsDefaults.showThumbnailTag); + + set showThumbnailTag(bool newValue) => setAndNotify(showThumbnailTagKey, newValue); + bool get showThumbnailLocation => getBoolOrDefault(showThumbnailLocationKey, SettingsDefaults.showThumbnailLocation); set showThumbnailLocation(bool newValue) => setAndNotify(showThumbnailLocationKey, newValue); @@ -694,6 +699,7 @@ class Settings extends ChangeNotifier { case confirmMoveUndatedItemsKey: case setMetadataDateBeforeFileOpKey: case showThumbnailFavouriteKey: + case showThumbnailTagKey: case showThumbnailLocationKey: case showThumbnailMotionPhotoKey: case showThumbnailRatingKey: diff --git a/lib/widgets/common/grid/theme.dart b/lib/widgets/common/grid/theme.dart index 670c9995e..6708de32b 100644 --- a/lib/widgets/common/grid/theme.dart +++ b/lib/widgets/common/grid/theme.dart @@ -34,6 +34,7 @@ class GridTheme extends StatelessWidget { showMotionPhoto: settings.showThumbnailMotionPhoto, showRating: settings.showThumbnailRating, showRaw: settings.showThumbnailRaw, + showTag: settings.showThumbnailTag, showTrash: showTrash ?? true, showVideoDuration: settings.showThumbnailVideoDuration, ); @@ -45,7 +46,7 @@ class GridTheme extends StatelessWidget { class GridThemeData { final double iconSize, fontSize, highlightBorderWidth; - final bool showFavourite, showLocation, showMotionPhoto, showRating, showRaw, showTrash, showVideoDuration; + final bool showFavourite, showLocation, showMotionPhoto, showRating, showRaw, showTag, showTrash, showVideoDuration; const GridThemeData({ required this.iconSize, @@ -56,6 +57,7 @@ class GridThemeData { required this.showMotionPhoto, required this.showRating, required this.showRaw, + required this.showTag, required this.showTrash, required this.showVideoDuration, }); diff --git a/lib/widgets/common/identity/aves_icons.dart b/lib/widgets/common/identity/aves_icons.dart index 53448ec14..93d4e8a4a 100644 --- a/lib/widgets/common/identity/aves_icons.dart +++ b/lib/widgets/common/identity/aves_icons.dart @@ -87,6 +87,21 @@ class FavouriteIcon extends StatelessWidget { } } +class TagIcon extends StatelessWidget { + const TagIcon({Key? key}) : super(key: key); + + static const scale = .9; + + @override + Widget build(BuildContext context) { + return const OverlayIcon( + icon: AIcons.tag, + iconScale: scale, + relativeOffset: Offset(.05, .05), + ); + } +} + class GpsIcon extends StatelessWidget { const GpsIcon({Key? key}) : super(key: key); @@ -204,7 +219,8 @@ class OverlayIcon extends StatelessWidget { final IconData icon; final String? text; final double iconScale; - final EdgeInsets margin; + final EdgeInsetsGeometry margin; + final Offset? relativeOffset; const OverlayIcon({ Key? key, @@ -213,25 +229,36 @@ class OverlayIcon extends StatelessWidget { this.text, // default margin for multiple icons in a `Column` this.margin = const EdgeInsets.only(left: 1, right: 1, bottom: 1), + this.relativeOffset, }) : super(key: key); @override Widget build(BuildContext context) { final size = context.select((t) => t.iconSize); - final iconChild = Icon( + Widget iconChild = Icon( icon, size: size, ); - final iconBox = SizedBox( + + if (relativeOffset != null) { + iconChild = FractionalTranslation( + translation: relativeOffset!, + child: iconChild, + ); + } + + if (iconScale != 1) { + // using a transform is better than modifying the icon size to properly center the scaled icon + iconChild = Transform.scale( + scale: iconScale, + child: iconChild, + ); + } + + iconChild = SizedBox( width: size, height: size, - // using a transform is better than modifying the icon size to properly center the scaled icon - child: iconScale != 1 - ? Transform.scale( - scale: iconScale, - child: iconChild, - ) - : iconChild, + child: iconChild, ); return Container( @@ -242,12 +269,12 @@ class OverlayIcon extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(size)), ), child: text == null - ? iconBox + ? iconChild : Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ - iconBox, + iconChild, const SizedBox(width: 2), Text( text!, diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index d3859d1bd..e6f61bf8e 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -20,6 +20,7 @@ class ThumbnailEntryOverlay extends StatelessWidget { Widget build(BuildContext context) { final children = [ if (entry.isFavourite && context.select((t) => t.showFavourite)) const FavouriteIcon(), + if (entry.tags.isNotEmpty && context.select((t) => t.showTag)) const TagIcon(), if (entry.hasGps && context.select((t) => t.showLocation)) const GpsIcon(), if (entry.rating != 0 && context.select((t) => t.showRating)) RatingIcon(entry: entry), if (entry.isVideo) diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index 5de58497a..1ba9221a5 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -37,6 +37,19 @@ class ThumbnailOverlayPage extends StatelessWidget { ), ), ), + SettingsSwitchListTile( + selector: (context, s) => s.showThumbnailTag, + onChanged: (v) => settings.showThumbnailTag = v, + title: context.l10n.settingsThumbnailShowTagIcon, + trailing: Padding( + padding: EdgeInsets.symmetric(horizontal: iconSize * (1 - TagIcon.scale) / 2), + child: Icon( + AIcons.tag, + size: iconSize * TagIcon.scale, + color: iconColor, + ), + ), + ), SettingsSwitchListTile( selector: (context, s) => s.showThumbnailLocation, onChanged: (v) => settings.showThumbnailLocation = v, diff --git a/test_driver/driver_screenshots.dart b/test_driver/driver_screenshots.dart index e7817792f..2691e9314 100644 --- a/test_driver/driver_screenshots.dart +++ b/test_driver/driver_screenshots.dart @@ -27,6 +27,7 @@ Future configureAndLaunch() async { ..collectionSortFactor = EntrySortFactor.date ..collectionBrowsingQuickActions = SettingsDefaults.collectionBrowsingQuickActions ..showThumbnailFavourite = false + ..showThumbnailTag = false ..showThumbnailLocation = false ..hiddenFilters = {} // viewer diff --git a/untranslated.json b/untranslated.json index eae752520..667a704f1 100644 --- a/untranslated.json +++ b/untranslated.json @@ -2,7 +2,8 @@ "de": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "es": [ @@ -18,25 +19,29 @@ "appPickDialogNone", "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "fr": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "id": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "it": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "ja": [ @@ -52,30 +57,35 @@ "appPickDialogNone", "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "ko": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "pt": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "ru": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ], "zh": [ "settingsSearchFieldLabel", "settingsSearchEmpty", - "settingsShowBottomNavigationBar" + "settingsShowBottomNavigationBar", + "settingsThumbnailShowTagIcon" ] }