#406 viewer: show rating & tags on overlay
This commit is contained in:
parent
eb0e51ed57
commit
92639a7066
20 changed files with 326 additions and 189 deletions
|
@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Viewer: optionally show rating & tags on overlay
|
||||||
- Viewer: long press on rating quick action for quicker rating
|
- Viewer: long press on rating quick action for quicker rating
|
||||||
- Search: missing address filter
|
- Search: missing address filter
|
||||||
- Lithuanian translation (thanks Gediminas Murauskas)
|
- Lithuanian translation (thanks Gediminas Murauskas)
|
||||||
|
|
|
@ -717,6 +717,7 @@
|
||||||
"settingsViewerShowMinimap": "Show minimap",
|
"settingsViewerShowMinimap": "Show minimap",
|
||||||
"settingsViewerShowInformation": "Show information",
|
"settingsViewerShowInformation": "Show information",
|
||||||
"settingsViewerShowInformationSubtitle": "Show title, date, location, etc.",
|
"settingsViewerShowInformationSubtitle": "Show title, date, location, etc.",
|
||||||
|
"settingsViewerShowRatingTags": "Show rating & tags",
|
||||||
"settingsViewerShowShootingDetails": "Show shooting details",
|
"settingsViewerShowShootingDetails": "Show shooting details",
|
||||||
"settingsViewerShowOverlayThumbnails": "Show thumbnails",
|
"settingsViewerShowOverlayThumbnails": "Show thumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect": "Blur effect",
|
"settingsViewerEnableOverlayBlurEffect": "Blur effect",
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:aves/ref/mime_types.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
||||||
import 'package:aves/theme/colors.dart';
|
import 'package:aves/theme/colors.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart';
|
import 'package:aves/widgets/viewer/info/metadata/metadata_dir.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -109,7 +110,10 @@ extension ExtraAvesEntryInfo on AvesEntry {
|
||||||
for (final stream in knownStreams) {
|
for (final stream in knownStreams) {
|
||||||
final index = (stream[Keys.index] ?? 0) + 1;
|
final index = (stream[Keys.index] ?? 0) + 1;
|
||||||
final typeText = getTypeText(stream);
|
final typeText = getTypeText(stream);
|
||||||
final dirName = 'Stream ${index.toString().padLeft(indexDigits, '0')} • $typeText';
|
final dirName = [
|
||||||
|
'Stream ${index.toString().padLeft(indexDigits, '0')}',
|
||||||
|
typeText,
|
||||||
|
].join(Constants.separator);
|
||||||
final formattedStreamTags = VideoMetadataFormatter.formatInfo(stream);
|
final formattedStreamTags = VideoMetadataFormatter.formatInfo(stream);
|
||||||
if (formattedStreamTags.isNotEmpty) {
|
if (formattedStreamTags.isNotEmpty) {
|
||||||
final color = colors.fromString(typeText);
|
final color = colors.fromString(typeText);
|
||||||
|
|
|
@ -79,6 +79,7 @@ class SettingsDefaults {
|
||||||
static const showOverlayOnOpening = true;
|
static const showOverlayOnOpening = true;
|
||||||
static const showOverlayMinimap = false;
|
static const showOverlayMinimap = false;
|
||||||
static const showOverlayInfo = true;
|
static const showOverlayInfo = true;
|
||||||
|
static const showOverlayRatingTags = false;
|
||||||
static const showOverlayShootingDetails = false;
|
static const showOverlayShootingDetails = false;
|
||||||
static const showOverlayThumbnailPreview = false;
|
static const showOverlayThumbnailPreview = false;
|
||||||
static const viewerGestureSideTapNext = false;
|
static const viewerGestureSideTapNext = false;
|
||||||
|
|
|
@ -103,6 +103,7 @@ class Settings extends ChangeNotifier {
|
||||||
static const showOverlayOnOpeningKey = 'show_overlay_on_opening';
|
static const showOverlayOnOpeningKey = 'show_overlay_on_opening';
|
||||||
static const showOverlayMinimapKey = 'show_overlay_minimap';
|
static const showOverlayMinimapKey = 'show_overlay_minimap';
|
||||||
static const showOverlayInfoKey = 'show_overlay_info';
|
static const showOverlayInfoKey = 'show_overlay_info';
|
||||||
|
static const showOverlayRatingTagsKey = 'show_overlay_rating_tags';
|
||||||
static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details';
|
static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details';
|
||||||
static const showOverlayThumbnailPreviewKey = 'show_overlay_thumbnail_preview';
|
static const showOverlayThumbnailPreviewKey = 'show_overlay_thumbnail_preview';
|
||||||
static const viewerGestureSideTapNextKey = 'viewer_gesture_side_tap_next';
|
static const viewerGestureSideTapNextKey = 'viewer_gesture_side_tap_next';
|
||||||
|
@ -507,6 +508,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set showOverlayInfo(bool newValue) => setAndNotify(showOverlayInfoKey, newValue);
|
set showOverlayInfo(bool newValue) => setAndNotify(showOverlayInfoKey, newValue);
|
||||||
|
|
||||||
|
bool get showOverlayRatingTags => getBool(showOverlayRatingTagsKey) ?? SettingsDefaults.showOverlayRatingTags;
|
||||||
|
|
||||||
|
set showOverlayRatingTags(bool newValue) => setAndNotify(showOverlayRatingTagsKey, newValue);
|
||||||
|
|
||||||
bool get showOverlayShootingDetails => getBool(showOverlayShootingDetailsKey) ?? SettingsDefaults.showOverlayShootingDetails;
|
bool get showOverlayShootingDetails => getBool(showOverlayShootingDetailsKey) ?? SettingsDefaults.showOverlayShootingDetails;
|
||||||
|
|
||||||
set showOverlayShootingDetails(bool newValue) => setAndNotify(showOverlayShootingDetailsKey, newValue);
|
set showOverlayShootingDetails(bool newValue) => setAndNotify(showOverlayShootingDetailsKey, newValue);
|
||||||
|
@ -932,6 +937,7 @@ class Settings extends ChangeNotifier {
|
||||||
case showOverlayOnOpeningKey:
|
case showOverlayOnOpeningKey:
|
||||||
case showOverlayMinimapKey:
|
case showOverlayMinimapKey:
|
||||||
case showOverlayInfoKey:
|
case showOverlayInfoKey:
|
||||||
|
case showOverlayRatingTagsKey:
|
||||||
case showOverlayShootingDetailsKey:
|
case showOverlayShootingDetailsKey:
|
||||||
case showOverlayThumbnailPreviewKey:
|
case showOverlayThumbnailPreviewKey:
|
||||||
case viewerGestureSideTapNextKey:
|
case viewerGestureSideTapNextKey:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
abstract class EmbeddedDataService {
|
abstract class EmbeddedDataService {
|
||||||
|
@ -37,7 +38,7 @@ class PlatformEmbeddedDataService implements EmbeddedDataService {
|
||||||
'mimeType': entry.mimeType,
|
'mimeType': entry.mimeType,
|
||||||
'uri': entry.uri,
|
'uri': entry.uri,
|
||||||
'sizeBytes': entry.sizeBytes,
|
'sizeBytes': entry.sizeBytes,
|
||||||
'displayName': '${entry.bestTitle} • Video',
|
'displayName': ['${entry.bestTitle}', 'Video'].join(Constants.separator),
|
||||||
});
|
});
|
||||||
if (result != null) return result as Map;
|
if (result != null) return result as Map;
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
|
@ -51,7 +52,7 @@ class PlatformEmbeddedDataService implements EmbeddedDataService {
|
||||||
try {
|
try {
|
||||||
final result = await _platform.invokeMethod('extractVideoEmbeddedPicture', <String, dynamic>{
|
final result = await _platform.invokeMethod('extractVideoEmbeddedPicture', <String, dynamic>{
|
||||||
'uri': entry.uri,
|
'uri': entry.uri,
|
||||||
'displayName': '${entry.bestTitle} • Cover',
|
'displayName': ['${entry.bestTitle}', 'Cover'].join(Constants.separator),
|
||||||
});
|
});
|
||||||
if (result != null) return result as Map;
|
if (result != null) return result as Map;
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
|
@ -67,7 +68,7 @@ class PlatformEmbeddedDataService implements EmbeddedDataService {
|
||||||
'mimeType': entry.mimeType,
|
'mimeType': entry.mimeType,
|
||||||
'uri': entry.uri,
|
'uri': entry.uri,
|
||||||
'sizeBytes': entry.sizeBytes,
|
'sizeBytes': entry.sizeBytes,
|
||||||
'displayName': '${entry.bestTitle} • $props',
|
'displayName': ['${entry.bestTitle}', '$props'].join(Constants.separator),
|
||||||
'propPath': props,
|
'propPath': props,
|
||||||
'propMimeType': propMimeType,
|
'propMimeType': propMimeType,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
String formatDay(DateTime date, String locale) => DateFormat.yMMMd(locale).format(date);
|
String formatDay(DateTime date, String locale) => DateFormat.yMMMd(locale).format(date);
|
||||||
|
|
||||||
String formatTime(DateTime date, String locale, bool use24hour) => (use24hour ? DateFormat.Hm(locale) : DateFormat.jm(locale)).format(date);
|
String formatTime(DateTime date, String locale, bool use24hour) => (use24hour ? DateFormat.Hm(locale) : DateFormat.jm(locale)).format(date);
|
||||||
|
|
||||||
String formatDateTime(DateTime date, String locale, bool use24hour) => '${formatDay(date, locale)} • ${formatTime(date, locale, use24hour)}';
|
String formatDateTime(DateTime date, String locale, bool use24hour) => [
|
||||||
|
formatDay(date, locale),
|
||||||
|
formatTime(date, locale, use24hour),
|
||||||
|
].join(Constants.separator);
|
||||||
|
|
||||||
String formatFriendlyDuration(Duration d) {
|
String formatFriendlyDuration(Duration d) {
|
||||||
final seconds = (d.inSeconds.remainder(Duration.secondsPerMinute)).toString().padLeft(2, '0');
|
final seconds = (d.inSeconds.remainder(Duration.secondsPerMinute)).toString().padLeft(2, '0');
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
class Constants {
|
class Constants {
|
||||||
|
static const separator = ' • ';
|
||||||
|
|
||||||
// `Color(0x00FFFFFF)` is different from `Color(0x00000000)` (or `Colors.transparent`)
|
// `Color(0x00FFFFFF)` is different from `Color(0x00000000)` (or `Colors.transparent`)
|
||||||
// when used in gradients or lerping to it
|
// when used in gradients or lerping to it
|
||||||
static const transparentWhite = Color(0x00FFFFFF);
|
static const transparentWhite = Color(0x00FFFFFF);
|
||||||
|
|
|
@ -138,7 +138,7 @@ class _RandomTextSpanHighlighterState extends State<_RandomTextSpanHighlighter>
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
...widget.spans.expandIndexed((i, v) => [
|
...widget.spans.expandIndexed((i, v) => [
|
||||||
if (i != 0) const TextSpan(text: ' • '),
|
if (i != 0) const TextSpan(text: Constants.separator),
|
||||||
TextSpan(text: v, style: i == _highlightedIndex ? _animatedStyle.value : _baseStyle),
|
TextSpan(text: v, style: i == _highlightedIndex ? _animatedStyle.value : _baseStyle),
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
|
|
|
@ -30,6 +30,18 @@ class ViewerOverlayPage extends StatelessWidget {
|
||||||
title: context.l10n.settingsViewerShowInformation,
|
title: context.l10n.settingsViewerShowInformation,
|
||||||
subtitle: context.l10n.settingsViewerShowInformationSubtitle,
|
subtitle: context.l10n.settingsViewerShowInformationSubtitle,
|
||||||
),
|
),
|
||||||
|
Selector<Settings, Tuple2<bool, bool>>(
|
||||||
|
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayRatingTags),
|
||||||
|
builder: (context, s, child) {
|
||||||
|
final showInfo = s.item1;
|
||||||
|
final current = s.item2;
|
||||||
|
return SwitchListTile(
|
||||||
|
value: current,
|
||||||
|
onChanged: showInfo ? (v) => settings.showOverlayRatingTags = v : null,
|
||||||
|
title: Text(context.l10n.settingsViewerShowRatingTags),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
Selector<Settings, Tuple2<bool, bool>>(
|
Selector<Settings, Tuple2<bool, bool>>(
|
||||||
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayShootingDetails),
|
selector: (context, s) => Tuple2(s.showOverlayInfo, s.showOverlayShootingDetails),
|
||||||
builder: (context, s, child) {
|
builder: (context, s, child) {
|
||||||
|
|
44
lib/widgets/viewer/overlay/details/date.dart
Normal file
44
lib/widgets/viewer/overlay/details/date.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/theme/format.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/details.dart';
|
||||||
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class OverlayDateRow extends StatelessWidget {
|
||||||
|
final AvesEntry entry;
|
||||||
|
final MultiPageController? multiPageController;
|
||||||
|
|
||||||
|
const OverlayDateRow({
|
||||||
|
super.key,
|
||||||
|
required this.entry,
|
||||||
|
required this.multiPageController,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final locale = context.l10n.localeName;
|
||||||
|
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||||
|
|
||||||
|
final date = entry.bestDate;
|
||||||
|
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
||||||
|
final resolutionText = entry.isSvg
|
||||||
|
? entry.aspectRatioText
|
||||||
|
: entry.isSized
|
||||||
|
? entry.resolutionText
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
DecoratedIcon(AIcons.date, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
|
||||||
|
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
|
||||||
|
Expanded(flex: 3, child: Text(dateText, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
Expanded(flex: 2, child: Text(resolutionText, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,30 +2,21 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/metadata/overlay.dart';
|
import 'package:aves/model/metadata/overlay.dart';
|
||||||
import 'package:aves/model/multipage.dart';
|
|
||||||
import 'package:aves/model/settings/enums/coordinate_format.dart';
|
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/theme/format.dart';
|
|
||||||
import 'package:aves/theme/icons.dart';
|
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
|
||||||
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/date.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/location.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/position_title.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/rating_tags.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/shooting.dart';
|
||||||
import 'package:aves/widgets/viewer/page_entry_builder.dart';
|
import 'package:aves/widgets/viewer/page_entry_builder.dart';
|
||||||
import 'package:decorated_icon/decorated_icon.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
const double _iconPadding = 8.0;
|
|
||||||
const double _iconSize = 16.0;
|
|
||||||
const double _interRowPadding = 2.0;
|
|
||||||
const double _subRowMinWidth = 300.0;
|
|
||||||
|
|
||||||
List<Shadow>? _shadows(BuildContext context) => Theme.of(context).brightness == Brightness.dark ? Constants.embossShadows : null;
|
|
||||||
|
|
||||||
class ViewerDetailOverlay extends StatefulWidget {
|
class ViewerDetailOverlay extends StatefulWidget {
|
||||||
final List<AvesEntry> entries;
|
final List<AvesEntry> entries;
|
||||||
final int index;
|
final int index;
|
||||||
|
@ -124,7 +115,13 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
final double availableWidth;
|
final double availableWidth;
|
||||||
final MultiPageController? multiPageController;
|
final MultiPageController? multiPageController;
|
||||||
|
|
||||||
|
static const double _interRowPadding = 2.0;
|
||||||
|
static const double _subRowMinWidth = 300.0;
|
||||||
static const padding = EdgeInsets.symmetric(vertical: 4, horizontal: 8);
|
static const padding = EdgeInsets.symmetric(vertical: 4, horizontal: 8);
|
||||||
|
static const double iconPadding = 8.0;
|
||||||
|
static const double iconSize = 16.0;
|
||||||
|
|
||||||
|
static List<Shadow>? shadows(BuildContext context) => Theme.of(context).brightness == Brightness.dark ? Constants.embossShadows : null;
|
||||||
|
|
||||||
const ViewerDetailOverlayContent({
|
const ViewerDetailOverlayContent({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -138,15 +135,16 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final infoMaxWidth = availableWidth - padding.horizontal;
|
final infoMaxWidth = availableWidth - padding.horizontal;
|
||||||
|
final showRatingTags = settings.showOverlayRatingTags;
|
||||||
final showShooting = settings.showOverlayShootingDetails;
|
final showShooting = settings.showOverlayShootingDetails;
|
||||||
|
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: pageEntry.metadataChangeNotifier,
|
animation: pageEntry.metadataChangeNotifier,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final positionTitle = _PositionTitleRow(entry: pageEntry, collectionPosition: position, multiPageController: multiPageController);
|
final positionTitle = OverlayPositionTitleRow(entry: pageEntry, collectionPosition: position, multiPageController: multiPageController);
|
||||||
return DefaultTextStyle(
|
return DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
shadows: _shadows(context),
|
shadows: shadows(context),
|
||||||
),
|
),
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
|
@ -185,6 +183,9 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
if (!collapsedLocation) {
|
if (!collapsedLocation) {
|
||||||
rows.add(_buildLocationFullRow(context));
|
rows.add(_buildLocationFullRow(context));
|
||||||
}
|
}
|
||||||
|
if (showRatingTags) {
|
||||||
|
rows.add(_buildRatingTagsFullRow(context));
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -201,18 +202,24 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildDateSubRow(double subRowWidth) => SizedBox(
|
Widget _buildDateSubRow(double subRowWidth) => SizedBox(
|
||||||
width: subRowWidth,
|
width: subRowWidth,
|
||||||
child: _DateRow(
|
child: OverlayDateRow(
|
||||||
entry: pageEntry,
|
entry: pageEntry,
|
||||||
multiPageController: multiPageController,
|
multiPageController: multiPageController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildRatingTagsFullRow(BuildContext context) => _buildFullRowSwitcher(
|
||||||
|
context: context,
|
||||||
|
visible: pageEntry.rating != 0 || pageEntry.tags.isNotEmpty,
|
||||||
|
builder: (context) => OverlayRatingTagsRow(entry: pageEntry),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildShootingFullRow(BuildContext context, double subRowWidth) => _buildFullRowSwitcher(
|
Widget _buildShootingFullRow(BuildContext context, double subRowWidth) => _buildFullRowSwitcher(
|
||||||
context: context,
|
context: context,
|
||||||
visible: details != null && details!.isNotEmpty,
|
visible: details != null && details!.isNotEmpty,
|
||||||
builder: (context) => SizedBox(
|
builder: (context) => SizedBox(
|
||||||
width: subRowWidth,
|
width: subRowWidth,
|
||||||
child: _ShootingRow(details!),
|
child: OverlayShootingRow(details: details!),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -220,20 +227,20 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
context: context,
|
context: context,
|
||||||
subRowWidth: subRowWidth,
|
subRowWidth: subRowWidth,
|
||||||
visible: details != null && details!.isNotEmpty,
|
visible: details != null && details!.isNotEmpty,
|
||||||
builder: (context) => _ShootingRow(details!),
|
builder: (context) => OverlayShootingRow(details: details!),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLocationFullRow(BuildContext context) => _buildFullRowSwitcher(
|
Widget _buildLocationFullRow(BuildContext context) => _buildFullRowSwitcher(
|
||||||
context: context,
|
context: context,
|
||||||
visible: pageEntry.hasGps,
|
visible: pageEntry.hasGps,
|
||||||
builder: (context) => _LocationRow(entry: pageEntry),
|
builder: (context) => OverlayLocationRow(entry: pageEntry),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLocationSubRow(BuildContext context, double subRowWidth) => _buildSubRowSwitcher(
|
Widget _buildLocationSubRow(BuildContext context, double subRowWidth) => _buildSubRowSwitcher(
|
||||||
context: context,
|
context: context,
|
||||||
subRowWidth: subRowWidth,
|
subRowWidth: subRowWidth,
|
||||||
visible: pageEntry.hasGps,
|
visible: pageEntry.hasGps,
|
||||||
builder: (context) => _LocationRow(entry: pageEntry),
|
builder: (context) => OverlayLocationRow(entry: pageEntry),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSubRowSwitcher({
|
Widget _buildSubRowSwitcher({
|
||||||
|
@ -283,146 +290,3 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LocationRow extends AnimatedWidget {
|
|
||||||
final AvesEntry entry;
|
|
||||||
|
|
||||||
_LocationRow({
|
|
||||||
required this.entry,
|
|
||||||
}) : super(listenable: entry.addressChangeNotifier);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
late final String location;
|
|
||||||
if (entry.hasAddress) {
|
|
||||||
location = entry.shortAddress;
|
|
||||||
} else {
|
|
||||||
final latLng = entry.latLng;
|
|
||||||
if (latLng != null) {
|
|
||||||
location = settings.coordinateFormat.format(context.l10n, latLng);
|
|
||||||
} else {
|
|
||||||
location = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
DecoratedIcon(AIcons.location, size: _iconSize, shadows: _shadows(context)),
|
|
||||||
const SizedBox(width: _iconPadding),
|
|
||||||
Expanded(child: Text(location, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PositionTitleRow extends StatelessWidget {
|
|
||||||
final AvesEntry entry;
|
|
||||||
final String? collectionPosition;
|
|
||||||
final MultiPageController? multiPageController;
|
|
||||||
|
|
||||||
const _PositionTitleRow({
|
|
||||||
required this.entry,
|
|
||||||
required this.collectionPosition,
|
|
||||||
required this.multiPageController,
|
|
||||||
});
|
|
||||||
|
|
||||||
String? get title => entry.bestTitle;
|
|
||||||
|
|
||||||
bool get isNotEmpty => collectionPosition != null || multiPageController != null || title != null;
|
|
||||||
|
|
||||||
static const separator = ' • ';
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Text toText({String? pagePosition}) => Text(
|
|
||||||
[
|
|
||||||
if (collectionPosition != null) collectionPosition,
|
|
||||||
if (pagePosition != null) pagePosition,
|
|
||||||
if (title != null) '${Constants.fsi}$title${Constants.pdi}',
|
|
||||||
].join(separator),
|
|
||||||
strutStyle: Constants.overflowStrutStyle);
|
|
||||||
|
|
||||||
if (multiPageController == null) return toText();
|
|
||||||
|
|
||||||
return StreamBuilder<MultiPageInfo?>(
|
|
||||||
stream: multiPageController!.infoStream,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final multiPageInfo = multiPageController!.info;
|
|
||||||
String? pagePosition;
|
|
||||||
if (multiPageInfo != null) {
|
|
||||||
// page count may be 0 when we know an entry to have multiple pages
|
|
||||||
// but fail to get information about these pages
|
|
||||||
final pageCount = multiPageInfo.pageCount;
|
|
||||||
if (pageCount > 0) {
|
|
||||||
final page = multiPageInfo.getById(entry.pageId ?? entry.id) ?? multiPageInfo.defaultPage;
|
|
||||||
pagePosition = '${(page?.index ?? 0) + 1}/$pageCount';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return toText(pagePosition: pagePosition);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DateRow extends StatelessWidget {
|
|
||||||
final AvesEntry entry;
|
|
||||||
final MultiPageController? multiPageController;
|
|
||||||
|
|
||||||
const _DateRow({
|
|
||||||
required this.entry,
|
|
||||||
required this.multiPageController,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final locale = context.l10n.localeName;
|
|
||||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
|
||||||
|
|
||||||
final date = entry.bestDate;
|
|
||||||
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
|
||||||
final resolutionText = entry.isSvg
|
|
||||||
? entry.aspectRatioText
|
|
||||||
: entry.isSized
|
|
||||||
? entry.resolutionText
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
DecoratedIcon(AIcons.date, size: _iconSize, shadows: _shadows(context)),
|
|
||||||
const SizedBox(width: _iconPadding),
|
|
||||||
Expanded(flex: 3, child: Text(dateText, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
Expanded(flex: 2, child: Text(resolutionText, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ShootingRow extends StatelessWidget {
|
|
||||||
final OverlayMetadata details;
|
|
||||||
|
|
||||||
const _ShootingRow(this.details);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final locale = context.l10n.localeName;
|
|
||||||
|
|
||||||
final aperture = details.aperture;
|
|
||||||
final apertureText = aperture != null ? 'ƒ/${NumberFormat('0.0', locale).format(aperture)}' : Constants.overlayUnknown;
|
|
||||||
|
|
||||||
final focalLength = details.focalLength;
|
|
||||||
final focalLengthText = focalLength != null ? context.l10n.focalLength(NumberFormat('0.#', locale).format(focalLength)) : Constants.overlayUnknown;
|
|
||||||
|
|
||||||
final iso = details.iso;
|
|
||||||
final isoText = iso != null ? 'ISO$iso' : Constants.overlayUnknown;
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
DecoratedIcon(AIcons.shooting, size: _iconSize, shadows: _shadows(context)),
|
|
||||||
const SizedBox(width: _iconPadding),
|
|
||||||
Expanded(child: Text(apertureText, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
Expanded(child: Text(details.exposureTime ?? Constants.overlayUnknown, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
Expanded(child: Text(focalLengthText, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
Expanded(child: Text(isoText, strutStyle: Constants.overflowStrutStyle)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
40
lib/widgets/viewer/overlay/details/location.dart
Normal file
40
lib/widgets/viewer/overlay/details/location.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/settings/enums/coordinate_format.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/details.dart';
|
||||||
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OverlayLocationRow extends AnimatedWidget {
|
||||||
|
final AvesEntry entry;
|
||||||
|
|
||||||
|
OverlayLocationRow({
|
||||||
|
super.key,
|
||||||
|
required this.entry,
|
||||||
|
}) : super(listenable: entry.addressChangeNotifier);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
late final String location;
|
||||||
|
if (entry.hasAddress) {
|
||||||
|
location = entry.shortAddress;
|
||||||
|
} else {
|
||||||
|
final latLng = entry.latLng;
|
||||||
|
if (latLng != null) {
|
||||||
|
location = settings.coordinateFormat.format(context.l10n, latLng);
|
||||||
|
} else {
|
||||||
|
location = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
DecoratedIcon(AIcons.location, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
|
||||||
|
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
|
||||||
|
Expanded(child: Text(location, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
53
lib/widgets/viewer/overlay/details/position_title.dart
Normal file
53
lib/widgets/viewer/overlay/details/position_title.dart
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/multipage.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OverlayPositionTitleRow extends StatelessWidget {
|
||||||
|
final AvesEntry entry;
|
||||||
|
final String? collectionPosition;
|
||||||
|
final MultiPageController? multiPageController;
|
||||||
|
|
||||||
|
const OverlayPositionTitleRow({
|
||||||
|
super.key,
|
||||||
|
required this.entry,
|
||||||
|
required this.collectionPosition,
|
||||||
|
required this.multiPageController,
|
||||||
|
});
|
||||||
|
|
||||||
|
String? get title => entry.bestTitle;
|
||||||
|
|
||||||
|
bool get isNotEmpty => collectionPosition != null || multiPageController != null || title != null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Text toText({String? pagePosition}) => Text(
|
||||||
|
[
|
||||||
|
if (collectionPosition != null) collectionPosition,
|
||||||
|
if (pagePosition != null) pagePosition,
|
||||||
|
if (title != null) '${Constants.fsi}$title${Constants.pdi}',
|
||||||
|
].join(Constants.separator),
|
||||||
|
strutStyle: Constants.overflowStrutStyle);
|
||||||
|
|
||||||
|
if (multiPageController == null) return toText();
|
||||||
|
|
||||||
|
return StreamBuilder<MultiPageInfo?>(
|
||||||
|
stream: multiPageController!.infoStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final multiPageInfo = multiPageController!.info;
|
||||||
|
String? pagePosition;
|
||||||
|
if (multiPageInfo != null) {
|
||||||
|
// page count may be 0 when we know an entry to have multiple pages
|
||||||
|
// but fail to get information about these pages
|
||||||
|
final pageCount = multiPageInfo.pageCount;
|
||||||
|
if (pageCount > 0) {
|
||||||
|
final page = multiPageInfo.getById(entry.pageId ?? entry.id) ?? multiPageInfo.defaultPage;
|
||||||
|
pagePosition = '${(page?.index ?? 0) + 1}/$pageCount';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toText(pagePosition: pagePosition);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
50
lib/widgets/viewer/overlay/details/rating_tags.dart
Normal file
50
lib/widgets/viewer/overlay/details/rating_tags.dart
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/details.dart';
|
||||||
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OverlayRatingTagsRow extends AnimatedWidget {
|
||||||
|
final AvesEntry entry;
|
||||||
|
|
||||||
|
OverlayRatingTagsRow({
|
||||||
|
super.key,
|
||||||
|
required this.entry,
|
||||||
|
}) : super(listenable: entry.metadataChangeNotifier);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final String ratingString;
|
||||||
|
final rating = entry.rating.clamp(-1, 5);
|
||||||
|
switch (rating) {
|
||||||
|
case -1:
|
||||||
|
ratingString = context.l10n.filterRatingRejectedLabel;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
ratingString = '';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ratingString = '${'★' * rating}${'☆' * (5 - rating)}';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final tags = entry.tags.join(Constants.separator);
|
||||||
|
final hasTags = tags.isNotEmpty;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
if (ratingString.isNotEmpty) ...[
|
||||||
|
Text(ratingString),
|
||||||
|
if (hasTags) const Text(Constants.separator),
|
||||||
|
],
|
||||||
|
if (hasTags) ...[
|
||||||
|
DecoratedIcon(AIcons.tag, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
|
||||||
|
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
|
||||||
|
Expanded(child: Text(tags)),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
39
lib/widgets/viewer/overlay/details/shooting.dart
Normal file
39
lib/widgets/viewer/overlay/details/shooting.dart
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import 'package:aves/model/metadata/overlay.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/viewer/overlay/details/details.dart';
|
||||||
|
import 'package:decorated_icon/decorated_icon.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class OverlayShootingRow extends StatelessWidget {
|
||||||
|
final OverlayMetadata details;
|
||||||
|
|
||||||
|
const OverlayShootingRow({super.key, required this.details});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final locale = context.l10n.localeName;
|
||||||
|
|
||||||
|
final aperture = details.aperture;
|
||||||
|
final apertureText = aperture != null ? 'ƒ/${NumberFormat('0.0', locale).format(aperture)}' : Constants.overlayUnknown;
|
||||||
|
|
||||||
|
final focalLength = details.focalLength;
|
||||||
|
final focalLengthText = focalLength != null ? context.l10n.focalLength(NumberFormat('0.#', locale).format(focalLength)) : Constants.overlayUnknown;
|
||||||
|
|
||||||
|
final iso = details.iso;
|
||||||
|
final isoText = iso != null ? 'ISO$iso' : Constants.overlayUnknown;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
DecoratedIcon(AIcons.shooting, size: ViewerDetailOverlayContent.iconSize, shadows: ViewerDetailOverlayContent.shadows(context)),
|
||||||
|
const SizedBox(width: ViewerDetailOverlayContent.iconPadding),
|
||||||
|
Expanded(child: Text(apertureText, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
Expanded(child: Text(details.exposureTime ?? Constants.overlayUnknown, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
Expanded(child: Text(focalLengthText, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
Expanded(child: Text(isoText, strutStyle: Constants.overflowStrutStyle)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/themes.dart';
|
import 'package:aves/theme/themes.dart';
|
||||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||||
import 'package:aves/widgets/viewer/multipage/conductor.dart';
|
import 'package:aves/widgets/viewer/multipage/conductor.dart';
|
||||||
import 'package:aves/widgets/viewer/overlay/details.dart';
|
import 'package:aves/widgets/viewer/overlay/details/details.dart';
|
||||||
import 'package:aves/widgets/viewer/overlay/minimap.dart';
|
import 'package:aves/widgets/viewer/overlay/minimap.dart';
|
||||||
import 'package:aves/widgets/viewer/page_entry_builder.dart';
|
import 'package:aves/widgets/viewer/page_entry_builder.dart';
|
||||||
import 'package:aves/widgets/viewer/visual/conductor.dart';
|
import 'package:aves/widgets/viewer/visual/conductor.dart';
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/app_flavor.dart';
|
||||||
import 'package:aves/model/settings/defaults.dart';
|
import 'package:aves/model/settings/defaults.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/common/basic/markdown_container.dart';
|
import 'package:aves/widgets/common/basic/markdown_container.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_logo.dart';
|
import 'package:aves/widgets/common/identity/aves_logo.dart';
|
||||||
|
@ -155,7 +156,7 @@ class _WelcomePageState extends State<WelcomePage> {
|
||||||
value: settings.isInstalledAppAccessAllowed,
|
value: settings.isInstalledAppAccessAllowed,
|
||||||
onChanged: (v) => setState(() => settings.isInstalledAppAccessAllowed = v),
|
onChanged: (v) => setState(() => settings.isInstalledAppAccessAllowed = v),
|
||||||
title: Text(l10n.settingsAllowInstalledAppAccess),
|
title: Text(l10n.settingsAllowInstalledAppAccess),
|
||||||
subtitle: Text([l10n.welcomeOptional, l10n.settingsAllowInstalledAppAccessSubtitle].join(' • ')),
|
subtitle: Text([l10n.welcomeOptional, l10n.settingsAllowInstalledAppAccessSubtitle].join(Constants.separator)),
|
||||||
contentPadding: contentPadding,
|
contentPadding: contentPadding,
|
||||||
),
|
),
|
||||||
if (canEnableErrorReporting)
|
if (canEnableErrorReporting)
|
||||||
|
|
|
@ -46,6 +46,7 @@ Future<void> configureAndLaunch() async {
|
||||||
..showOverlayOnOpening = true
|
..showOverlayOnOpening = true
|
||||||
..showOverlayMinimap = false
|
..showOverlayMinimap = false
|
||||||
..showOverlayInfo = true
|
..showOverlayInfo = true
|
||||||
|
..showOverlayRatingTags = false
|
||||||
..showOverlayShootingDetails = false
|
..showOverlayShootingDetails = false
|
||||||
..showOverlayThumbnailPreview = false
|
..showOverlayThumbnailPreview = false
|
||||||
..viewerUseCutout = true
|
..viewerUseCutout = true
|
||||||
|
|
|
@ -443,6 +443,7 @@
|
||||||
"settingsViewerShowMinimap",
|
"settingsViewerShowMinimap",
|
||||||
"settingsViewerShowInformation",
|
"settingsViewerShowInformation",
|
||||||
"settingsViewerShowInformationSubtitle",
|
"settingsViewerShowInformationSubtitle",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
|
@ -596,17 +597,20 @@
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
"widgetDisplayedItemMostRecent",
|
"widgetDisplayedItemMostRecent",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
"settingsSubtitleThemeTextPositionTile",
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
"settingsSubtitleThemeTextPositionDialogTitle",
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fa": [
|
"fa": [
|
||||||
|
@ -1053,6 +1057,7 @@
|
||||||
"settingsViewerShowMinimap",
|
"settingsViewerShowMinimap",
|
||||||
"settingsViewerShowInformation",
|
"settingsViewerShowInformation",
|
||||||
"settingsViewerShowInformationSubtitle",
|
"settingsViewerShowInformationSubtitle",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
|
@ -1201,7 +1206,8 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"gl": [
|
"gl": [
|
||||||
|
@ -1515,6 +1521,7 @@
|
||||||
"settingsViewerShowMinimap",
|
"settingsViewerShowMinimap",
|
||||||
"settingsViewerShowInformation",
|
"settingsViewerShowInformation",
|
||||||
"settingsViewerShowInformationSubtitle",
|
"settingsViewerShowInformationSubtitle",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
|
@ -1669,13 +1676,15 @@
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
"widgetDisplayedItemMostRecent",
|
"widgetDisplayedItemMostRecent",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
"settingsSubtitleThemeTextPositionTile",
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
"settingsSubtitleThemeTextPositionDialogTitle",
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ja": [
|
"ja": [
|
||||||
|
@ -1686,17 +1695,20 @@
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
"widgetDisplayedItemMostRecent",
|
"widgetDisplayedItemMostRecent",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
"settingsSubtitleThemeTextPositionTile",
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
"settingsSubtitleThemeTextPositionDialogTitle",
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ko": [
|
"ko": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"lt": [
|
"lt": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nb": [
|
"nb": [
|
||||||
|
@ -1788,6 +1800,7 @@
|
||||||
"settingsViewerOverlayTile",
|
"settingsViewerOverlayTile",
|
||||||
"settingsViewerOverlayPageTitle",
|
"settingsViewerOverlayPageTitle",
|
||||||
"settingsViewerShowOverlayOnOpening",
|
"settingsViewerShowOverlayOnOpening",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
"settingsSlideshowShuffle",
|
"settingsSlideshowShuffle",
|
||||||
|
@ -1828,6 +1841,7 @@
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
"widgetDisplayedItemMostRecent",
|
"widgetDisplayedItemMostRecent",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
"settingsSubtitleThemeTextPositionTile",
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
"settingsSubtitleThemeTextPositionDialogTitle",
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
|
@ -2184,6 +2198,7 @@
|
||||||
"settingsViewerShowMinimap",
|
"settingsViewerShowMinimap",
|
||||||
"settingsViewerShowInformation",
|
"settingsViewerShowInformation",
|
||||||
"settingsViewerShowInformationSubtitle",
|
"settingsViewerShowInformationSubtitle",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
|
@ -2338,17 +2353,20 @@
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
"widgetDisplayedItemMostRecent",
|
"widgetDisplayedItemMostRecent",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
"settingsSubtitleThemeTextPositionTile",
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
"settingsSubtitleThemeTextPositionDialogTitle",
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ro": [
|
"ro": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
"filterNoAddressLabel"
|
"filterNoAddressLabel",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"th": [
|
"th": [
|
||||||
|
@ -2575,6 +2593,7 @@
|
||||||
"settingsViewerShowMinimap",
|
"settingsViewerShowMinimap",
|
||||||
"settingsViewerShowInformation",
|
"settingsViewerShowInformation",
|
||||||
"settingsViewerShowInformationSubtitle",
|
"settingsViewerShowInformationSubtitle",
|
||||||
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowShootingDetails",
|
"settingsViewerShowShootingDetails",
|
||||||
"settingsViewerShowOverlayThumbnails",
|
"settingsViewerShowOverlayThumbnails",
|
||||||
"settingsViewerEnableOverlayBlurEffect",
|
"settingsViewerEnableOverlayBlurEffect",
|
||||||
|
@ -2723,20 +2742,14 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"tr": [
|
"tr": [
|
||||||
"entryInfoActionExportMetadata",
|
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
"subtitlePositionTop",
|
"settingsViewerShowRatingTags"
|
||||||
"subtitlePositionBottom",
|
|
||||||
"widgetDisplayedItemRandom",
|
|
||||||
"widgetDisplayedItemMostRecent",
|
|
||||||
"settingsSubtitleThemeTextPositionTile",
|
|
||||||
"settingsSubtitleThemeTextPositionDialogTitle",
|
|
||||||
"settingsWidgetDisplayedItem"
|
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
"aboutLicensesFlutterPackagesSectionTitle",
|
"aboutLicensesFlutterPackagesSectionTitle",
|
||||||
"aboutLicensesDartPackagesSectionTitle"
|
"aboutLicensesDartPackagesSectionTitle",
|
||||||
|
"settingsViewerShowRatingTags"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue