diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/iap/devtools_options.yaml b/iap/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/iap/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/application.dart b/lib/application.dart index e272b92..87387c2 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -12,7 +12,10 @@ import 'package:lightmeter/screens/equipment_profiles/screen_equipment_profiles. import 'package:lightmeter/screens/film_edit/flow_film_edit.dart'; import 'package:lightmeter/screens/films/screen_films.dart'; import 'package:lightmeter/screens/lightmeter_pro/screen_lightmeter_pro.dart'; +import 'package:lightmeter/screens/logbook/screen_logbook.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/flow_logbook_photo_edit.dart'; import 'package:lightmeter/screens/metering/flow_metering.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:lightmeter/screens/settings/flow_settings.dart'; import 'package:lightmeter/screens/shared/release_notes_dialog/flow_dialog_release_notes.dart'; import 'package:lightmeter/screens/timer/flow_timer.dart'; @@ -51,13 +54,15 @@ class Application extends StatelessWidget { routes: { NavigationRoutes.meteringScreen.name: (_) => const ReleaseNotesFlow(child: MeteringFlow()), NavigationRoutes.settingsScreen.name: (_) => const SettingsFlow(), - NavigationRoutes.equipmentProfilesListScreen.name: (_) => const EquipmentProfilesScreen(), + NavigationRoutes.equipmentProfilesListScreen.name: (_) => const LogbookScreen(), NavigationRoutes.equipmentProfileEditScreen.name: (context) => EquipmentProfileEditFlow(args: context.routeArgs()), NavigationRoutes.filmsListScreen.name: (_) => const FilmsScreen(), NavigationRoutes.filmEditScreen.name: (context) => FilmEditFlow(args: context.routeArgs()), NavigationRoutes.proFeaturesScreen.name: (_) => LightmeterProScreen(), NavigationRoutes.timerScreen.name: (context) => TimerFlow(args: context.routeArgs()), + NavigationRoutes.logbookPhotoEditScreen.name: (context) => + LogbookPhotoEditFlow(args: LogbookPhotoEditArgs(photo: context.routeArgs())), }, ), ); diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 1169425..87ad0c3 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -55,10 +55,12 @@ "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "Alle", "apertureValues": "Blend-Werte", + "apertureValue": "Blend-Wert", "apertureValuesFilterDescription": "Wähle die anzuzeigenden Blend-Werte aus. Die Werte sind normalerweise von dem verwendeten Objektiv bestimmt.", "ndFilters": "ND Filter", "ndFiltersFilterDescription": "Wähle die anzuzeigenden ND Filter aus. (Beispielsweise die Meistverwendeten)", "shutterSpeedValues": "Belichtungszeiten", + "shutterSpeedValue": "Belichtungszeit", "shutterSpeedValuesFilterDescription": "Wähle die anzuzeigenden Belichtungszeiten aus. Die Werte sind normalerweise von der Kamera bestimmt.", "shutterSpeedManualShort": "B", "shutterSpeedManual": "Manuell", @@ -163,5 +165,11 @@ "filmFormulaExponentialRfPlaceholder": "1.3", "name": "Name", "addEquipmentProfileTitle": "Ausrüstung hinzufügen", - "editEquipmentProfileTitle": "Ausrüstung bearbeiten" + "editEquipmentProfileTitle": "Ausrüstung bearbeiten", + "editPhotoTitle": "Foto bearbeiten", + "date": "Datum", + "ndFilter": "ND Filter", + "film": "Film", + "note": "Notiz", + "notSet": "Nicht gesetzt" } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index eb5df18..66f3ca9 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -55,10 +55,12 @@ "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "All", "apertureValues": "Aperture values", + "apertureValue": "Aperture value", "apertureValuesFilterDescription": "Select the range of aperture values to display. This is usually determined by the lens you are using.", "ndFilters": "ND filters", "ndFiltersFilterDescription": "Select the ND filters to display. These may be your most commonly used ND filters or the ones that fit your lens.", "shutterSpeedValues": "Shutter speed values", + "shutterSpeedValue": "Shutter speed value", "shutterSpeedValuesFilterDescription": "Select the range of shutter speed values to display. This is usually determined by the camera body you are using.", "shutterSpeedManualShort": "B", "shutterSpeedManual": "Manual", @@ -163,5 +165,11 @@ "filmFormulaExponentialRfPlaceholder": "1.3", "name": "Name", "addEquipmentProfileTitle": "Add equipment", - "editEquipmentProfileTitle": "Edit equipment" + "editEquipmentProfileTitle": "Edit equipment", + "editPhotoTitle": "Edit Photo", + "date": "Date", + "ndFilter": "ND Filter", + "film": "Film", + "note": "Note", + "notSet": "Not set" } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 2ef2281..115b11a 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -55,10 +55,12 @@ "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "Tout", "apertureValues": "Valeurs Aperture", + "apertureValue": "Valeur Aperture", "apertureValuesFilterDescription": "Sélectionnez la plage de valeurs d'ouverture à afficher. Cela est généralement déterminé par l'objectif que vous utilisez.", "ndFilters": "Filtres ND", "ndFiltersFilterDescription": "Sélectionnez les filtres ND à afficher. Ce sont peut-être vos filtres ND les plus couramment utilisés ou ceux qui correspondent à votre lentille.", "shutterSpeedValues": "Valeurs de la vitesse d'obturation", + "shutterSpeedValue": "Valeur de la vitesse d'obturation", "shutterSpeedValuesFilterDescription": "Sélectionnez la plage de valeurs de vitesse d'obturation à afficher. Cela est généralement déterminé par le corps de l'appareil que vous utilisez.", "shutterSpeedManualShort": "B", "shutterSpeedManual": "Manuelle", @@ -154,5 +156,11 @@ "filmFormulaExponentialRfPlaceholder": "1.3", "name": "Titre", "addEquipmentProfileTitle": "Ajouter un profil", - "editEquipmentProfileTitle": "Editer le profil" + "editEquipmentProfileTitle": "Editer le profil", + "editPhotoTitle": "Modifier la photo", + "date": "Date", + "ndFilter": "Filtre ND", + "film": "Film", + "note": "Note", + "notSet": "Non défini" } \ No newline at end of file diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index b25e8a3..2aca4df 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -55,10 +55,12 @@ "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "Все", "apertureValues": "Значения диафрагмы", + "apertureValue": "Значение диафрагмы", "apertureValuesFilterDescription": "Выберите диапазон значений диафрагмы для отображения. Обычно определяется объективом, который вы используете.", "ndFilters": "ND фильтры", "ndFiltersFilterDescription": "Выберите ND фильтры для отображения. Это могут быть наиболее часто используемые ND фильтры или фильтры, подходящие под ваш объектив.", "shutterSpeedValues": "Значения выдержки", + "shutterSpeedValue": "Значение выдержки", "shutterSpeedValuesFilterDescription": "Выберите диапазон значений выдержки. Обычно ограничивается возможностями вашей камеры.", "shutterSpeedManualShort": "B", "shutterSpeedManual": "Ручная", @@ -153,5 +155,11 @@ "filmFormulaExponentialRfPlaceholder": "1.3", "name": "Название", "addEquipmentProfileTitle": "Добавить профиль", - "editEquipmentProfileTitle": "Редактировать профиль" + "editEquipmentProfileTitle": "Редактировать профиль", + "editPhotoTitle": "Редактировать фото", + "date": "Дата", + "ndFilter": "ND фильтр", + "film": "Плёнка", + "note": "Заметка", + "notSet": "Не задано" } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 19ed915..b9d5531 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -55,10 +55,12 @@ "equipmentProfileNameHint": "Praktica MTL5B", "equipmentProfileAllValues": "全部", "apertureValues": "光圈值", + "apertureValue": "光圈值", "apertureValuesFilterDescription": "选择要显示的光圈值范围。取决于使用的镜头。", "ndFilters": "ND 滤镜", "ndFiltersFilterDescription": "选择要显示的 ND 滤镜系数。取决于使用的 ND 滤镜", "shutterSpeedValues": "快门速度", + "shutterSpeedValue": "快门速度", "shutterSpeedValuesFilterDescription": "选择要显示的快门速度范围。取决于相机机身。", "shutterSpeedManualShort": "B门", "shutterSpeedManual": "手动", @@ -151,5 +153,11 @@ "filmFormulaExponentialRf": "Rf", "filmFormulaExponentialRfPlaceholder": "1.3", "addEquipmentProfileTitle": "添加设备", - "editEquipmentProfileTitle": "编辑设备" + "editEquipmentProfileTitle": "编辑设备", + "editPhotoTitle": "编辑照片", + "date": "日期", + "ndFilter": "ND 滤镜", + "film": "胶片", + "note": "备注", + "notSet": "未设置" } \ No newline at end of file diff --git a/lib/navigation/routes.dart b/lib/navigation/routes.dart index 420d90f..f40b764 100644 --- a/lib/navigation/routes.dart +++ b/lib/navigation/routes.dart @@ -7,4 +7,5 @@ enum NavigationRoutes { filmEditScreen, proFeaturesScreen, timerScreen, + logbookPhotoEditScreen, } diff --git a/lib/screens/logbook/screen_logbook.dart b/lib/screens/logbook/screen_logbook.dart index 399542a..34b4aa8 100644 --- a/lib/screens/logbook/screen_logbook.dart +++ b/lib/screens/logbook/screen_logbook.dart @@ -36,7 +36,12 @@ class _LogbookScreenState extends State with SingleTickerProvider ); } - void _editProfile(LogbookPhoto photo) {} + void _editProfile(LogbookPhoto photo) { + Navigator.of(context).pushNamed( + NavigationRoutes.logbookPhotoEditScreen.name, + arguments: photo, + ); + } } class _PicturesGridBuilder extends StatelessWidget { @@ -62,10 +67,13 @@ class _PicturesGridBuilder extends StatelessWidget { ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { - return Container( - alignment: Alignment.center, - color: Colors.teal[100 * (index % 9)], - child: Image.file(File(values[index].name)), + return GestureDetector( + onTap: () => onEdit(values[index]), + child: Container( + alignment: Alignment.center, + color: Colors.teal[100 * (index % 9)], + child: Image.file(File(values[index].name)), + ), ); }, childCount: values.length, diff --git a/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart new file mode 100644 index 0000000..461ac13 --- /dev/null +++ b/lib/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart @@ -0,0 +1,96 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/providers/logbook_photos_provider.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/event_logbook_photo_edit.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class LogbookPhotoEditBloc extends Bloc { + final LogbookPhotosProviderState photosProvider; + final LogbookPhoto _originalPhoto; + LogbookPhoto _newPhoto; + + LogbookPhotoEditBloc( + this.photosProvider, + LogbookPhoto photo, + ) : _originalPhoto = photo, + _newPhoto = photo, + super( + LogbookPhotoEditState( + id: photo.id, + name: photo.name, + timestamp: photo.timestamp, + ev: photo.ev, + iso: photo.iso, + nd: photo.nd, + film: photo.film, + coordinates: photo.coordinates, + aperture: null, + shutterSpeed: null, + note: photo.note, + canSave: false, + ), + ) { + on( + (event, emit) async { + switch (event) { + case final LogbookPhotoApertureChangedEvent e: + await _onApertureChanged(e, emit); + case final LogbookPhotoShutterSpeedChangedEvent e: + await _onShutterSpeedChanged(e, emit); + case final LogbookPhotoNoteChangedEvent e: + await _onNoteChanged(e, emit); + case LogbookPhotoSaveEvent(): + await _onSave(event, emit); + case LogbookPhotoDeleteEvent(): + await _onDelete(event, emit); + } + }, + ); + } + + Future _onApertureChanged(LogbookPhotoApertureChangedEvent event, Emitter emit) async { + // For now, we'll just update the state since LogbookPhoto doesn't support aperture + emit( + state.copyWith( + aperture: event.aperture, + canSave: _canSave(), + ), + ); + } + + Future _onShutterSpeedChanged(LogbookPhotoShutterSpeedChangedEvent event, Emitter emit) async { + // For now, we'll just update the state since LogbookPhoto doesn't support shutterSpeed + emit( + state.copyWith( + shutterSpeed: event.shutterSpeed, + canSave: _canSave(), + ), + ); + } + + Future _onNoteChanged(LogbookPhotoNoteChangedEvent event, Emitter emit) async { + _newPhoto = _newPhoto.copyWith(note: event.note); + emit( + state.copyWith( + note: event.note, + canSave: _canSave(), + ), + ); + } + + Future _onSave(LogbookPhotoSaveEvent _, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + await photosProvider.updateProfile(_newPhoto); + emit(state.copyWith(isLoading: false)); + } + + Future _onDelete(LogbookPhotoDeleteEvent _, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + await photosProvider.deleteProfile(_newPhoto); + emit(state.copyWith(isLoading: false)); + } + + bool _canSave() { + return _newPhoto != _originalPhoto; + } +} diff --git a/lib/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart b/lib/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart new file mode 100644 index 0000000..6ba9cd8 --- /dev/null +++ b/lib/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class PickerListTile extends StatelessWidget { + final IconData icon; + final String title; + final T? selectedValue; + final List values; + final ValueChanged onChanged; + + const PickerListTile({ + required this.icon, + required this.title, + required this.selectedValue, + required this.values, + required this.onChanged, + super.key, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Icon(icon), + title: Text(title), + trailing: Text(selectedValue?.toString() ?? S.of(context).notSet), + onTap: () { + showDialog( + context: context, + builder: (_) => DialogPicker( + icon: icon, + title: title, + selectedValue: selectedValue, + values: [null, ...values], + titleAdapter: (context, value) => value?.toString() ?? S.of(context).notSet, + ), + ).then((value) { + if (value != null) { + onChanged(value); + } + }); + }, + ); + } +} diff --git a/lib/screens/logbook_photo_edit/event_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/event_logbook_photo_edit.dart new file mode 100644 index 0000000..8773d09 --- /dev/null +++ b/lib/screens/logbook_photo_edit/event_logbook_photo_edit.dart @@ -0,0 +1,31 @@ +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +sealed class LogbookPhotoEditEvent { + const LogbookPhotoEditEvent(); +} + +class LogbookPhotoApertureChangedEvent extends LogbookPhotoEditEvent { + final ApertureValue? aperture; + + const LogbookPhotoApertureChangedEvent(this.aperture); +} + +class LogbookPhotoShutterSpeedChangedEvent extends LogbookPhotoEditEvent { + final ShutterSpeedValue? shutterSpeed; + + const LogbookPhotoShutterSpeedChangedEvent(this.shutterSpeed); +} + +class LogbookPhotoNoteChangedEvent extends LogbookPhotoEditEvent { + final String? note; + + const LogbookPhotoNoteChangedEvent(this.note); +} + +class LogbookPhotoSaveEvent extends LogbookPhotoEditEvent { + const LogbookPhotoSaveEvent(); +} + +class LogbookPhotoDeleteEvent extends LogbookPhotoEditEvent { + const LogbookPhotoDeleteEvent(); +} diff --git a/lib/screens/logbook_photo_edit/flow_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/flow_logbook_photo_edit.dart new file mode 100644 index 0000000..de0189b --- /dev/null +++ b/lib/screens/logbook_photo_edit/flow_logbook_photo_edit.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/providers/logbook_photos_provider.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class LogbookPhotoEditArgs { + final LogbookPhoto photo; + + const LogbookPhotoEditArgs({required this.photo}); +} + +class LogbookPhotoEditFlow extends StatelessWidget { + final LogbookPhotoEditArgs args; + + const LogbookPhotoEditFlow({ + required this.args, + super.key, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => LogbookPhotoEditBloc( + LogbookPhotosProvider.of(context), + args.photo, + ), + child: const LogbookPhotoEditScreen(), + ); + } +} diff --git a/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart new file mode 100644 index 0000000..51e95c7 --- /dev/null +++ b/lib/screens/logbook_photo_edit/screen_logbook_photo_edit.dart @@ -0,0 +1,263 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/platform_config.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/components/picker_list_tile/widget_list_tile_picker.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/event_logbook_photo_edit.dart'; +import 'package:lightmeter/screens/logbook_photo_edit/state_logbook_photo_edit.dart'; +import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; +import 'package:lightmeter/screens/shared/text_field/widget_text_field.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class LogbookPhotoEditScreen extends StatefulWidget { + const LogbookPhotoEditScreen({super.key}); + + @override + State createState() => _LogbookPhotoEditScreenState(); +} + +class _LogbookPhotoEditScreenState extends State { + @override + Widget build(BuildContext context) { + return BlocConsumer( + listenWhen: (previous, current) => previous.isLoading != current.isLoading, + listener: (context, state) { + if (state.isLoading) { + FocusScope.of(context).unfocus(); + } else { + Navigator.of(context).pop(); + } + }, + buildWhen: (previous, current) => previous.isLoading != current.isLoading, + builder: (context, state) => IgnorePointer( + ignoring: state.isLoading, + child: SliverScreen( + title: Text(S.of(context).editPhotoTitle), + appBarActions: [ + BlocBuilder( + buildWhen: (previous, current) => previous.canSave != current.canSave, + builder: (context, state) => IconButton( + onPressed: state.canSave + ? () { + context.read().add(const LogbookPhotoSaveEvent()); + } + : null, + icon: const Icon(Icons.save_outlined), + ), + ), + IconButton( + onPressed: () { + context.read().add(const LogbookPhotoDeleteEvent()); + }, + icon: const Icon(Icons.delete_outlined), + ), + ], + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB( + Dimens.paddingM, + 0, + Dimens.paddingM, + Dimens.paddingM, + ), + child: Column( + children: [ + const _PhotoPreviewBuilder(), + const SizedBox(height: Dimens.grid16), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), + child: Opacity( + opacity: state.isLoading ? Dimens.disabledOpacity : Dimens.enabledOpacity, + child: const Column( + children: [ + _DateListTile(), + _NoteListTile(), + _EvListTile(), + _IsoListTile(), + _NdFilterListTile(), + _AperturePickerListTile(), + _ShutterSpeedPickerListTile(), + ], + ), + ), + ), + ), + ], + ), + ), + ), + SliverToBoxAdapter(child: SizedBox(height: MediaQuery.paddingOf(context).bottom)), + ], + ), + ), + ); + } +} + +class _PhotoPreviewBuilder extends StatelessWidget { + const _PhotoPreviewBuilder(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (_, __) => false, + builder: (context, state) => AspectRatio( + aspectRatio: PlatformConfig.cameraPreviewAspectRatio, + child: ClipRRect( + borderRadius: BorderRadius.circular(Dimens.borderRadiusM), + child: Image.file( + File(state.name), + fit: BoxFit.cover, + ), + ), + ), + ); + } +} + +class _DateListTile extends StatelessWidget { + const _DateListTile(); + + String _formatDate(DateTime dateTime) { + final day = dateTime.day.toString().padLeft(2, '0'); + final month = dateTime.month.toString().padLeft(2, '0'); + final year = dateTime.year.toString(); + final hour = dateTime.hour.toString().padLeft(2, '0'); + final minute = dateTime.minute.toString().padLeft(2, '0'); + return '$day.$month.$year $hour:$minute'; + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (_, __) => false, + builder: (context, state) => ListTile( + leading: const Icon(Icons.access_time), + title: Text(S.of(context).date), + trailing: Text(_formatDate(state.timestamp)), + ), + ); + } +} + +class _NoteListTile extends StatelessWidget { + const _NoteListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) => Padding( + padding: const EdgeInsets.only( + left: Dimens.paddingM, + top: Dimens.paddingS / 2, + right: Dimens.paddingL, + bottom: Dimens.paddingS / 2, + ), + child: LightmeterTextField( + initialValue: state.note, + maxLength: 500, + hintText: S.of(context).note, + style: Theme.of(context).listTileTheme.titleTextStyle, + leading: const Icon(Icons.note_outlined), + onChanged: (value) { + context.read().add(LogbookPhotoNoteChangedEvent(value)); + }, + ), + ), + ); + } +} + +class _EvListTile extends StatelessWidget { + const _EvListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (_, __) => false, + builder: (context, state) => ListTile( + leading: const Icon(Icons.exposure), + title: Text(S.of(context).ev), + trailing: Text(state.ev.toStringAsFixed(1)), + ), + ); + } +} + +class _IsoListTile extends StatelessWidget { + const _IsoListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (_, __) => false, + builder: (context, state) => ListTile( + leading: const Icon(Icons.iso_outlined), + title: Text(S.of(context).iso), + trailing: Text(state.iso.toString()), + ), + ); + } +} + +class _NdFilterListTile extends StatelessWidget { + const _NdFilterListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (_, __) => false, + builder: (context, state) => ListTile( + leading: const Icon(Icons.filter_b_and_w_outlined), + title: Text(S.of(context).ndFilter), + trailing: Text(state.nd.toString()), + ), + ); + } +} + +class _AperturePickerListTile extends StatelessWidget { + const _AperturePickerListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) => previous.aperture != current.aperture, + builder: (context, state) => PickerListTile( + icon: Icons.camera_outlined, + title: S.of(context).apertureValue, + values: ApertureValue.values, + selectedValue: state.aperture, + onChanged: (value) { + context.read().add(LogbookPhotoApertureChangedEvent(value)); + }, + ), + ); + } +} + +class _ShutterSpeedPickerListTile extends StatelessWidget { + const _ShutterSpeedPickerListTile(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) => previous.shutterSpeed != current.shutterSpeed, + builder: (context, state) => PickerListTile( + icon: Icons.shutter_speed_outlined, + title: S.of(context).shutterSpeedValue, + values: ShutterSpeedValue.values, + selectedValue: state.shutterSpeed, + onChanged: (value) { + context.read().add(LogbookPhotoShutterSpeedChangedEvent(value)); + }, + ), + ); + } +} diff --git a/lib/screens/logbook_photo_edit/state_logbook_photo_edit.dart b/lib/screens/logbook_photo_edit/state_logbook_photo_edit.dart new file mode 100644 index 0000000..ed63dda --- /dev/null +++ b/lib/screens/logbook_photo_edit/state_logbook_photo_edit.dart @@ -0,0 +1,64 @@ +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +class LogbookPhotoEditState { + final String id; + final String name; + final DateTime timestamp; + final double ev; + final int iso; + final int nd; + final Film film; + final Coordinates? coordinates; + final ApertureValue? aperture; + final ShutterSpeedValue? shutterSpeed; + final String? note; + final bool canSave; + final bool isLoading; + + const LogbookPhotoEditState({ + required this.id, + required this.name, + required this.timestamp, + required this.ev, + required this.iso, + required this.nd, + required this.film, + this.coordinates, + this.aperture, + this.shutterSpeed, + this.note, + required this.canSave, + this.isLoading = false, + }); + + LogbookPhotoEditState copyWith({ + String? id, + String? name, + DateTime? timestamp, + double? ev, + int? iso, + int? nd, + Film? film, + Coordinates? coordinates, + ApertureValue? aperture, + ShutterSpeedValue? shutterSpeed, + String? note, + bool? canSave, + bool? isLoading, + }) => + LogbookPhotoEditState( + id: id ?? this.id, + name: name ?? this.name, + timestamp: timestamp ?? this.timestamp, + ev: ev ?? this.ev, + iso: iso ?? this.iso, + nd: nd ?? this.nd, + film: film ?? this.film, + coordinates: coordinates ?? this.coordinates, + aperture: aperture ?? this.aperture, + shutterSpeed: shutterSpeed ?? this.shutterSpeed, + note: note ?? this.note, + canSave: canSave ?? this.canSave, + isLoading: isLoading ?? this.isLoading, + ); +}