info: edit title

This commit is contained in:
Thibault Deckers 2022-09-01 11:52:15 +02:00
parent c2cc81fd1d
commit c753e4f7a2
24 changed files with 267 additions and 108 deletions

View file

@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased]
### Added
- Collection / Info: edit title via IPTC / XMP
### Changed
- upgraded Flutter to stable v3.3.0

View file

@ -427,8 +427,9 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
// - XMP / photoshop:DateCreated
// - PNG / TIME / LAST_MODIFICATION_TIME
// - Video / METADATA_KEY_DATE
// set `KEY_XMP_TITLE` from this field:
// set `KEY_XMP_TITLE` from these fields (by precedence):
// - XMP / dc:title
// - IPTC / object-name
// set `KEY_XMP_SUBJECTS` from these fields (by precedence):
// - XMP / dc:subject
// - IPTC / keywords
@ -567,11 +568,16 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
metadata.getDirectoriesOfType(XmpDirectory::class.java).map { it.xmpMeta }.forEach(::processXmp)
// XMP fallback to IPTC
if (!metadataMap.containsKey(KEY_XMP_SUBJECTS)) {
if (!metadataMap.containsKey(KEY_XMP_TITLE) || !metadataMap.containsKey(KEY_XMP_SUBJECTS)) {
for (dir in metadata.getDirectoriesOfType(IptcDirectory::class.java)) {
if (!metadataMap.containsKey(KEY_XMP_TITLE)) {
dir.getSafeString(IptcDirectory.TAG_OBJECT_NAME) { metadataMap[KEY_XMP_TITLE] = it }
}
if (!metadataMap.containsKey(KEY_XMP_SUBJECTS)) {
dir.keywords?.let { metadataMap[KEY_XMP_SUBJECTS] = it.joinToString(XMP_SUBJECTS_SEPARATOR) }
}
}
}
when (mimeType) {
MimeTypes.PNG -> {

View file

@ -87,7 +87,6 @@
"entryInfoActionEditDate": "Datum & Uhrzeit bearbeiten",
"entryInfoActionEditLocation": "Standort bearbeiten",
"entryInfoActionEditDescription": "Beschreibung bearbeiten",
"entryInfoActionEditRating": "Bewertung bearbeiten",
"entryInfoActionEditTags": "Tags bearbeiten",
"entryInfoActionRemoveMetadata": "Metadaten entfernen",
@ -259,8 +258,6 @@
"locationPickerUseThisLocationButton": "Diesen Standort verwenden",
"editEntryDescriptionDialogTitle": "Beschreibung",
"editEntryRatingDialogTitle": "Bewertung",
"removeEntryMetadataDialogTitle": "Entfernung von Metadaten",
@ -614,6 +611,7 @@
"viewerInfoBackToViewerTooltip": "Zurück zum Betrachter",
"viewerInfoUnknown": "Unbekannt",
"viewerInfoLabelDescription": "Beschreibung",
"viewerInfoLabelTitle": "Titel",
"viewerInfoLabelDate": "Datum",
"viewerInfoLabelResolution": "Auflösung",

View file

@ -115,7 +115,7 @@
"entryInfoActionEditDate": "Edit date & time",
"entryInfoActionEditLocation": "Edit location",
"entryInfoActionEditDescription": "Edit description",
"entryInfoActionEditTitleDescription": "Edit title & description",
"entryInfoActionEditRating": "Edit rating",
"entryInfoActionEditTags": "Edit tags",
"entryInfoActionRemoveMetadata": "Remove metadata",
@ -389,8 +389,6 @@
"locationPickerUseThisLocationButton": "Use this location",
"editEntryDescriptionDialogTitle": "Description",
"editEntryRatingDialogTitle": "Rating",
"removeEntryMetadataDialogTitle": "Metadata Removal",
@ -799,6 +797,7 @@
"viewerInfoBackToViewerTooltip": "Back to viewer",
"viewerInfoUnknown": "unknown",
"viewerInfoLabelDescription": "Description",
"viewerInfoLabelTitle": "Title",
"viewerInfoLabelDate": "Date",
"viewerInfoLabelResolution": "Resolution",

View file

@ -87,7 +87,7 @@
"entryInfoActionEditDate": "Modifier la date",
"entryInfoActionEditLocation": "Modifier le lieu",
"entryInfoActionEditDescription": "Modifier la description",
"entryInfoActionEditTitleDescription": "Modifier titre et description",
"entryInfoActionEditRating": "Modifier la notation",
"entryInfoActionEditTags": "Modifier les libellés",
"entryInfoActionRemoveMetadata": "Retirer les métadonnées",
@ -259,8 +259,6 @@
"locationPickerUseThisLocationButton": "Utiliser ce lieu",
"editEntryDescriptionDialogTitle": "Description",
"editEntryRatingDialogTitle": "Notation",
"removeEntryMetadataDialogTitle": "Retrait de métadonnées",
@ -614,6 +612,7 @@
"viewerInfoBackToViewerTooltip": "Retour à la visionneuse",
"viewerInfoUnknown": "inconnu",
"viewerInfoLabelDescription": "Description",
"viewerInfoLabelTitle": "Titre",
"viewerInfoLabelDate": "Date",
"viewerInfoLabelResolution": "Résolution",

View file

@ -87,7 +87,6 @@
"entryInfoActionEditDate": "Modifica data e ora",
"entryInfoActionEditLocation": "Modifica posizione",
"entryInfoActionEditDescription": "Modifica descrizione",
"entryInfoActionEditRating": "Modifica valutazione",
"entryInfoActionEditTags": "Modifica etichetta",
"entryInfoActionRemoveMetadata": "Rimuovi metadati",
@ -259,8 +258,6 @@
"locationPickerUseThisLocationButton": "Usa questa posizione",
"editEntryDescriptionDialogTitle": "Descrizione",
"editEntryRatingDialogTitle": "Valutazione",
"removeEntryMetadataDialogTitle": "Rimozione dei metadati",
@ -614,6 +611,7 @@
"viewerInfoBackToViewerTooltip": "Torna alla visualizzazione",
"viewerInfoUnknown": "sconosciuto",
"viewerInfoLabelDescription": "Descrizione",
"viewerInfoLabelTitle": "Titolo",
"viewerInfoLabelDate": "Data",
"viewerInfoLabelResolution": "Risoluzione",

View file

@ -87,7 +87,7 @@
"entryInfoActionEditDate": "날짜 및 시간 수정",
"entryInfoActionEditLocation": "위치 수정",
"entryInfoActionEditDescription": "설명 수정",
"entryInfoActionEditTitleDescription": "제목 및 설명 수정",
"entryInfoActionEditRating": "별점 수정",
"entryInfoActionEditTags": "태그 수정",
"entryInfoActionRemoveMetadata": "메타데이터 삭제",
@ -259,8 +259,6 @@
"locationPickerUseThisLocationButton": "이 위치 사용",
"editEntryDescriptionDialogTitle": "설명",
"editEntryRatingDialogTitle": "별점",
"removeEntryMetadataDialogTitle": "메타데이터 삭제",
@ -614,6 +612,7 @@
"viewerInfoBackToViewerTooltip": "뷰어로",
"viewerInfoUnknown": "알 수 없음",
"viewerInfoLabelDescription": "설명",
"viewerInfoLabelTitle": "제목",
"viewerInfoLabelDate": "날짜",
"viewerInfoLabelResolution": "해상도",

View file

@ -87,7 +87,6 @@
"entryInfoActionEditDate": "Bewerk Datum & Tijd",
"entryInfoActionEditLocation": "Bewerk Locatie",
"entryInfoActionEditDescription": "Omschrijving wijzigen",
"entryInfoActionEditRating": "Bewerk waardering",
"entryInfoActionEditTags": "Bewerk labels",
"entryInfoActionRemoveMetadata": "Verwijder metadata",
@ -259,8 +258,6 @@
"locationPickerUseThisLocationButton": "Gebruik deze locatie",
"editEntryDescriptionDialogTitle": "Omschrijving",
"editEntryRatingDialogTitle": "Beoordeling",
"removeEntryMetadataDialogTitle": "Verwijderen metadata",
@ -614,6 +611,7 @@
"viewerInfoBackToViewerTooltip": "Terug naar viewer",
"viewerInfoUnknown": "onbekendd",
"viewerInfoLabelDescription": "Omschrijving",
"viewerInfoLabelTitle": "Titel",
"viewerInfoLabelDate": "Datum",
"viewerInfoLabelResolution": "Resolutie",

View file

@ -87,7 +87,6 @@
"entryInfoActionEditDate": "Editar data e hora",
"entryInfoActionEditLocation": "Editar localização",
"entryInfoActionEditDescription": "Editar descrição",
"entryInfoActionEditRating": "Editar classificação",
"entryInfoActionEditTags": "Editar etiquetas",
"entryInfoActionRemoveMetadata": "Remover metadados",
@ -259,8 +258,6 @@
"locationPickerUseThisLocationButton": "Usar essa localização",
"editEntryDescriptionDialogTitle": "Descrição",
"editEntryRatingDialogTitle": "Avaliação",
"removeEntryMetadataDialogTitle": "Remoção de metadados",
@ -614,6 +611,7 @@
"viewerInfoBackToViewerTooltip": "Voltar ao visualizador",
"viewerInfoUnknown": "desconhecido",
"viewerInfoLabelDescription": "Descrição",
"viewerInfoLabelTitle": "Título",
"viewerInfoLabelDate": "Data",
"viewerInfoLabelResolution": "Resolução",

View file

@ -87,7 +87,6 @@
"entryInfoActionEditDate": "编辑日期和时间",
"entryInfoActionEditLocation": "编辑位置",
"entryInfoActionEditDescription": "编辑备注",
"entryInfoActionEditRating": "修改评分",
"entryInfoActionEditTags": "编辑标签",
"entryInfoActionRemoveMetadata": "移除元数据",
@ -259,8 +258,6 @@
"locationPickerUseThisLocationButton": "使用此位置",
"editEntryDescriptionDialogTitle": "备注",
"editEntryRatingDialogTitle": "评分",
"removeEntryMetadataDialogTitle": "元数据移除工具",
@ -614,6 +611,7 @@
"viewerInfoBackToViewerTooltip": "返回查看器",
"viewerInfoUnknown": "未知",
"viewerInfoLabelDescription": "备注",
"viewerInfoLabelTitle": "标题",
"viewerInfoLabelDate": "日期",
"viewerInfoLabelResolution": "分辨率",

View file

@ -7,7 +7,7 @@ enum EntryInfoAction {
// general
editDate,
editLocation,
editDescription,
editTitleDescription,
editRating,
editTags,
removeMetadata,
@ -24,7 +24,7 @@ class EntryInfoActions {
static const common = [
EntryInfoAction.editDate,
EntryInfoAction.editLocation,
EntryInfoAction.editDescription,
EntryInfoAction.editTitleDescription,
EntryInfoAction.editRating,
EntryInfoAction.editTags,
EntryInfoAction.removeMetadata,
@ -45,8 +45,8 @@ extension ExtraEntryInfoAction on EntryInfoAction {
return context.l10n.entryInfoActionEditDate;
case EntryInfoAction.editLocation:
return context.l10n.entryInfoActionEditLocation;
case EntryInfoAction.editDescription:
return context.l10n.entryInfoActionEditDescription;
case EntryInfoAction.editTitleDescription:
return context.l10n.entryInfoActionEditTitleDescription;
case EntryInfoAction.editRating:
return context.l10n.entryInfoActionEditRating;
case EntryInfoAction.editTags:
@ -88,7 +88,7 @@ extension ExtraEntryInfoAction on EntryInfoAction {
return AIcons.date;
case EntryInfoAction.editLocation:
return AIcons.location;
case EntryInfoAction.editDescription:
case EntryInfoAction.editTitleDescription:
return AIcons.description;
case EntryInfoAction.editRating:
return AIcons.editRating;

View file

@ -31,7 +31,7 @@ enum EntrySetAction {
flip,
editDate,
editLocation,
editDescription,
editTitleDescription,
editRating,
editTags,
removeMetadata,
@ -100,7 +100,7 @@ class EntrySetActions {
static const edit = [
EntrySetAction.editDate,
EntrySetAction.editLocation,
EntrySetAction.editDescription,
EntrySetAction.editTitleDescription,
EntrySetAction.editRating,
EntrySetAction.editTags,
EntrySetAction.removeMetadata,
@ -164,8 +164,8 @@ extension ExtraEntrySetAction on EntrySetAction {
return context.l10n.entryInfoActionEditDate;
case EntrySetAction.editLocation:
return context.l10n.entryInfoActionEditLocation;
case EntrySetAction.editDescription:
return context.l10n.entryInfoActionEditDescription;
case EntrySetAction.editTitleDescription:
return context.l10n.entryInfoActionEditTitleDescription;
case EntrySetAction.editRating:
return context.l10n.entryInfoActionEditRating;
case EntrySetAction.editTags:
@ -233,7 +233,7 @@ extension ExtraEntrySetAction on EntrySetAction {
return AIcons.date;
case EntrySetAction.editLocation:
return AIcons.location;
case EntrySetAction.editDescription:
case EntrySetAction.editTitleDescription:
return AIcons.description;
case EntrySetAction.editRating:
return AIcons.editRating;

View file

@ -279,7 +279,7 @@ class AvesEntry {
bool get canEditLocation => canEdit && canEditExif;
bool get canEditDescription => canEdit && (canEditExif || canEditXmp);
bool get canEditTitleDescription => canEdit && canEditXmp;
bool get canEditRating => canEdit && canEditXmp;

View file

@ -140,37 +140,62 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
return _changeOrientation(() => metadataEditService.flip(this));
}
// write:
// write title:
// - IPTC / object-name, if IPTC exists
// - XMP / dc:title
// write description:
// - Exif / ImageDescription
// - IPTC / caption-abstract, if IPTC exists
// - XMP / dc:description
Future<Set<EntryDataType>> editDescription(String? description) async {
Future<Set<EntryDataType>> editTitleDescription(Map<DescriptionField, String?> fields) async {
final Set<EntryDataType> dataTypes = {};
final Map<MetadataType, dynamic> metadata = {};
final missingDate = await _missingDateCheckAndExifEdit(dataTypes);
if (canEditExif) {
final editTitle = fields.keys.contains(DescriptionField.title);
final editDescription = fields.keys.contains(DescriptionField.description);
final title = fields[DescriptionField.title];
final description = fields[DescriptionField.description];
if (canEditExif && editDescription) {
metadata[MetadataType.exif] = {MetadataField.exifImageDescription.exifInterfaceTag!: description};
}
if (canEditIptc) {
final iptc = await metadataFetchService.getIptc(this);
if (iptc != null) {
if (editTitle) {
editIptcValues(iptc, IPTC.applicationRecord, IPTC.objectName, {if (title != null) title});
}
if (editDescription) {
editIptcValues(iptc, IPTC.applicationRecord, IPTC.captionAbstractTag, {if (description != null) description});
}
metadata[MetadataType.iptc] = iptc;
}
}
if (canEditXmp) {
metadata[MetadataType.xmp] = await _editXmp((descriptions) {
final modified = XMP.setAttribute(
var modified = false;
if (editTitle) {
modified |= XMP.setAttribute(
descriptions,
XMP.dcTitle,
title,
namespace: Namespaces.dc,
strat: XmpEditStrategy.always,
);
}
if (editDescription) {
modified |= XMP.setAttribute(
descriptions,
XMP.dcDescription,
description,
namespace: Namespaces.dc,
strat: XmpEditStrategy.always,
);
}
if (modified && missingDate != null) {
editCreateDateXmp(descriptions, missingDate);
}
@ -182,6 +207,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
if (newFields.isNotEmpty) {
dataTypes.addAll({
EntryDataType.basic,
EntryDataType.catalog,
});
}
@ -467,3 +493,5 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
};
}
}
enum DescriptionField { title, description }

View file

@ -2,6 +2,7 @@ class IPTC {
static const int applicationRecord = 2;
// ApplicationRecord tags
static const int objectName = 5;
static const int keywordsTag = 25;
static const int captionAbstractTag = 120;
}

View file

@ -153,6 +153,7 @@ class XMP {
static const containerDirectory = 'Directory';
static const dcDescription = 'description';
static const dcSubject = 'subject';
static const dcTitle = 'title';
static const msPhotoRating = 'Rating';
static const xmpRating = 'Rating';

View file

@ -496,7 +496,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
case EntrySetAction.flip:
case EntrySetAction.editDate:
case EntrySetAction.editLocation:
case EntrySetAction.editDescription:
case EntrySetAction.editTitleDescription:
case EntrySetAction.editRating:
case EntrySetAction.editTags:
case EntrySetAction.removeMetadata:

View file

@ -92,7 +92,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
case EntrySetAction.flip:
case EntrySetAction.editDate:
case EntrySetAction.editLocation:
case EntrySetAction.editDescription:
case EntrySetAction.editTitleDescription:
case EntrySetAction.editRating:
case EntrySetAction.editTags:
case EntrySetAction.removeMetadata:
@ -144,7 +144,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
case EntrySetAction.flip:
case EntrySetAction.editDate:
case EntrySetAction.editLocation:
case EntrySetAction.editDescription:
case EntrySetAction.editTitleDescription:
case EntrySetAction.editRating:
case EntrySetAction.editTags:
case EntrySetAction.removeMetadata:
@ -221,8 +221,8 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
case EntrySetAction.editLocation:
_editLocation(context);
break;
case EntrySetAction.editDescription:
_editDescription(context);
case EntrySetAction.editTitleDescription:
_editTitleDescription(context);
break;
case EntrySetAction.editRating:
_editRating(context);
@ -495,14 +495,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
await _edit(context, entries, (entry) => entry.editLocation(location));
}
Future<void> _editDescription(BuildContext context) async {
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditDescription);
Future<void> _editTitleDescription(BuildContext context) async {
final entries = await _getEditableTargetItems(context, canEdit: (entry) => entry.canEditTitleDescription);
if (entries == null || entries.isEmpty) return;
final description = await selectDescription(context, entries);
if (description == null) return;
final modifier = await selectTitleDescriptionModifier(context, entries);
if (modifier == null) return;
await _edit(context, entries, (entry) => entry.editDescription(description));
await _edit(context, entries, (entry) => entry.editTitleDescription(modifier));
}
Future<void> _editRating(BuildContext context) async {

View file

@ -1,4 +1,5 @@
import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_metadata_edition.dart';
import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/metadata/enums.dart';
import 'package:aves/model/source/collection_lens.dart';
@ -40,14 +41,17 @@ mixin EntryEditorMixin {
);
}
Future<String?> selectDescription(BuildContext context, Set<AvesEntry> entries) async {
Future<Map<DescriptionField, String?>?> selectTitleDescriptionModifier(BuildContext context, Set<AvesEntry> entries) async {
if (entries.isEmpty) return null;
final initialDescription = await metadataFetchService.getDescription(entries.first) ?? '';
final entry = entries.first;
final initialTitle = entry.catalogMetadata?.xmpTitle ?? '';
final initialDescription = await metadataFetchService.getDescription(entry) ?? '';
return showDialog<String>(
return showDialog<Map<DescriptionField, String?>>(
context: context,
builder: (context) => EditEntryDescriptionDialog(
builder: (context) => EditEntryTitleDescriptionDialog(
initialTitle: initialTitle,
initialDescription: initialDescription,
),
);

View file

@ -0,0 +1,55 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class LabeledCheckbox extends StatefulWidget {
final bool value;
final ValueChanged<bool?> onChanged;
final String text;
const LabeledCheckbox({
Key? key,
required this.value,
required this.onChanged,
required this.text,
}) : super(key: key);
@override
State<LabeledCheckbox> createState() => _LabeledCheckboxState();
}
class _LabeledCheckboxState extends State<LabeledCheckbox> {
late TapGestureRecognizer _tapRecognizer;
@override
void initState() {
super.initState();
_tapRecognizer = TapGestureRecognizer()..onTap = () => widget.onChanged(!widget.value);
}
@override
void dispose() {
_tapRecognizer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Checkbox(
value: widget.value,
onChanged: widget.onChanged,
),
),
TextSpan(
text: widget.text,
recognizer: _tapRecognizer,
),
],
),
);
}
}

View file

@ -1,52 +1,56 @@
import 'package:aves/model/entry_metadata_edition.dart';
import 'package:aves/widgets/common/basic/labeled_checkbox.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:flutter/material.dart';
class EditEntryDescriptionDialog extends StatefulWidget {
final String initialDescription;
class EditEntryTitleDescriptionDialog extends StatefulWidget {
final String initialTitle, initialDescription;
const EditEntryDescriptionDialog({
const EditEntryTitleDescriptionDialog({
super.key,
required this.initialTitle,
required this.initialDescription,
});
@override
State<EditEntryDescriptionDialog> createState() => _EditEntryDescriptionDialogState();
State<EditEntryTitleDescriptionDialog> createState() => _EditEntryTitleDescriptionDialogState();
}
class _EditEntryDescriptionDialogState extends State<EditEntryDescriptionDialog> {
late final TextEditingController _textController;
class _EditEntryTitleDescriptionDialogState extends State<EditEntryTitleDescriptionDialog> {
final Set<DescriptionField> fields = {
DescriptionField.title,
DescriptionField.description,
};
late final TextEditingController _titleTextController, _descriptionTextController;
@override
void initState() {
super.initState();
_textController = TextEditingController(text: widget.initialDescription);
_titleTextController = TextEditingController(text: widget.initialTitle);
_descriptionTextController = TextEditingController(text: widget.initialDescription);
}
@override
Widget build(BuildContext context) {
return MediaQueryDataProvider(
child: Builder(builder: (context) {
final l10n = context.l10n;
return AvesDialog(
title: l10n.editEntryDescriptionDialogTitle,
content: TextField(
controller: _textController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
maxLines: null,
),
scrollableContent: [
const SizedBox(height: 8),
..._buildFieldEditor(DescriptionField.title),
..._buildFieldEditor(DescriptionField.description),
const SizedBox(height: 8),
],
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
),
TextButton(
onPressed: () => _submit(context),
child: Text(l10n.applyButtonLabel),
onPressed: fields.isEmpty ? null : () => _submit(context),
child: Text(context.l10n.applyButtonLabel),
),
],
);
@ -54,5 +58,54 @@ class _EditEntryDescriptionDialogState extends State<EditEntryDescriptionDialog>
);
}
void _submit(BuildContext context) => Navigator.pop(context, _textController.text);
List<Widget> _buildFieldEditor(DescriptionField field) {
final editing = fields.contains(field);
return [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: LabeledCheckbox(
value: editing,
onChanged: (v) => setState(() => editing ? fields.remove(field) : fields.add(field)),
text: _fieldName(field),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: _fieldController(field),
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
maxLines: null,
enabled: editing,
),
),
];
}
TextEditingController _fieldController(DescriptionField field) {
switch (field) {
case DescriptionField.title:
return _titleTextController;
case DescriptionField.description:
return _descriptionTextController;
}
}
String _fieldName(DescriptionField field) {
switch (field) {
case DescriptionField.title:
return context.l10n.viewerInfoLabelTitle;
case DescriptionField.description:
return context.l10n.viewerInfoLabelDescription;
}
}
void _submit(BuildContext context) {
final modifier = Map.fromEntries(fields.map((field) {
final text = _fieldController(field).text;
return MapEntry(field, text.isEmpty ? null : text);
}));
return Navigator.pop<Map<DescriptionField, String?>>(context, modifier);
}
}

View file

@ -35,7 +35,7 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
// general
case EntryInfoAction.editDate:
case EntryInfoAction.editLocation:
case EntryInfoAction.editDescription:
case EntryInfoAction.editTitleDescription:
case EntryInfoAction.editRating:
case EntryInfoAction.editTags:
case EntryInfoAction.removeMetadata:
@ -60,8 +60,8 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
return entry.canEditDate;
case EntryInfoAction.editLocation:
return entry.canEditLocation;
case EntryInfoAction.editDescription:
return entry.canEditDescription;
case EntryInfoAction.editTitleDescription:
return entry.canEditTitleDescription;
case EntryInfoAction.editRating:
return entry.canEditRating;
case EntryInfoAction.editTags:
@ -92,8 +92,8 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
case EntryInfoAction.editLocation:
await _editLocation(context);
break;
case EntryInfoAction.editDescription:
await _editDescription(context);
case EntryInfoAction.editTitleDescription:
await _editTitleDescription(context);
break;
case EntryInfoAction.editRating:
await _editRating(context);
@ -137,11 +137,11 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
await edit(context, () => entry.editLocation(location));
}
Future<void> _editDescription(BuildContext context) async {
final description = await selectDescription(context, {entry});
if (description == null) return;
Future<void> _editTitleDescription(BuildContext context) async {
final modifier = await selectTitleDescriptionModifier(context, {entry});
if (modifier == null) return;
await edit(context, () => entry.editDescription(description));
await edit(context, () => entry.editTitleDescription(modifier));
}
Future<void> _editRating(BuildContext context) async {

View file

@ -55,7 +55,7 @@ class InfoSearchDelegate extends SearchDelegate {
final l10n = context.l10n;
final suggestions = {
l10n.viewerInfoSearchSuggestionDate: 'date or time or when -timer -uptime -exposure -timeline -verbatim',
l10n.viewerInfoSearchSuggestionDescription: 'abstract or description or comment or textual or title -line',
l10n.viewerInfoSearchSuggestionDescription: 'description or title or comment or textual or abstract or object name -line',
l10n.viewerInfoSearchSuggestionDimensions: 'width or height or dimension or framesize or imagelength',
l10n.viewerInfoSearchSuggestionResolution: 'resolution',
l10n.viewerInfoSearchSuggestionRights: 'rights or copyright or attribution or license or artist or creator or by-line or credit -tool',

View file

@ -1,43 +1,59 @@
{
"de": [
"entryInfoActionEditTitleDescription"
],
"es": [
"entryInfoActionEditDescription",
"entryInfoActionEditTitleDescription",
"filterRecentlyAddedLabel",
"editEntryDescriptionDialogTitle",
"settingsConfirmationAfterMoveToBinItems"
"settingsConfirmationAfterMoveToBinItems",
"viewerInfoLabelDescription"
],
"id": [
"entryInfoActionEditDescription",
"entryInfoActionEditTitleDescription",
"filterRecentlyAddedLabel",
"editEntryDescriptionDialogTitle",
"settingsConfirmationAfterMoveToBinItems",
"settingsViewerGestureSideTapNext"
"settingsViewerGestureSideTapNext",
"viewerInfoLabelDescription"
],
"it": [
"entryInfoActionEditTitleDescription"
],
"ja": [
"entryInfoActionEditDescription",
"entryInfoActionEditTitleDescription",
"filterRecentlyAddedLabel",
"editEntryDescriptionDialogTitle",
"settingsConfirmationAfterMoveToBinItems",
"settingsViewerGestureSideTapNext"
"settingsViewerGestureSideTapNext",
"viewerInfoLabelDescription"
],
"nl": [
"entryInfoActionEditTitleDescription"
],
"pt": [
"entryInfoActionEditTitleDescription"
],
"ru": [
"entryInfoActionEditDescription",
"entryInfoActionEditTitleDescription",
"filterOnThisDayLabel",
"filterRecentlyAddedLabel",
"editEntryDescriptionDialogTitle",
"settingsConfirmationAfterMoveToBinItems",
"settingsViewerGestureSideTapNext",
"settingsSlideshowFillScreen",
"settingsScreenSaverPageTitle",
"settingsWidgetShowOutline"
"settingsWidgetShowOutline",
"viewerInfoLabelDescription"
],
"tr": [
"slideshowActionResume",
"slideshowActionShowInCollection",
"entryInfoActionEditDescription",
"entryInfoActionEditTitleDescription",
"filterOnThisDayLabel",
"filterRecentlyAddedLabel",
"slideshowVideoPlaybackSkip",
@ -50,7 +66,6 @@
"wallpaperTargetHome",
"wallpaperTargetLock",
"wallpaperTargetHomeLock",
"editEntryDescriptionDialogTitle",
"menuActionSlideshow",
"settingsConfirmationAfterMoveToBinItems",
"settingsViewerGestureSideTapNext",
@ -67,6 +82,11 @@
"settingsSlideshowVideoPlaybackTitle",
"settingsScreenSaverPageTitle",
"settingsWidgetShowOutline",
"viewerSetWallpaperButtonLabel"
"viewerSetWallpaperButtonLabel",
"viewerInfoLabelDescription"
],
"zh": [
"entryInfoActionEditTitleDescription"
]
}