diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 35464c7..f5c1bd0 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -34,6 +34,8 @@ "calibrationMessage": "Die Messgenauigkeit hängt von der Gerätehardware ab. Testen Sie die App und kalibrieren Sie EV-Werte für beste Ergebnisse", "calibrationMessageCameraOnly": "Die Messgenauigkeit hängt von der Kamera ab. Testen Sie die App und kalibrieren Sie EV-Werte für beste Ergebnisse", "camera": "Kamera", + "pinholeCamera": "Lochkamera", + "equipmentProfileType": "Ausrüstungsprofiltyp", "lightSensor": "Lichtsensor", "showEv100": "EV\u2081\u2080\u2080 anzeigen", "meteringScreenLayout": "Messansicht Layout", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 9d2773e..e9447fa 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -34,6 +34,8 @@ "calibrationMessage": "Measurement accuracy depends on your device's hardware. Test the app and calibrate EV values for optimal results", "calibrationMessageCameraOnly": "Measurement accuracy depends on your device's camera. Test the app and calibrate EV values for optimal results", "camera": "Camera", + "pinholeCamera": "Pinhole Camera", + "equipmentProfileType": "Equipment profile type", "lightSensor": "Light sensor", "showEv100": "Show EV\u2081\u2080\u2080", "meteringScreenLayout": "Metering screen layout", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 913d0ed..2ee7a40 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -34,6 +34,8 @@ "calibrationMessage": "La précision dépend du matériel de l'appareil. Testez l'app et calibrez les valeurs EV pour de meilleurs résultats", "calibrationMessageCameraOnly": "La précision dépend de la caméra de l'appareil. Testez l'app et calibrez les valeurs EV pour de meilleurs résultats", "camera": "Caméra", + "pinholeCamera": "Sténopé", + "equipmentProfileType": "Type de profil d'équipement", "lightSensor": "Capteur de lumière", "showEv100": "Montrer EV\u2081\u2080\u2080", "meteringScreenLayout": "Disposition de l'écran de mesure", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 5aba72f..1fe5a8d 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -34,6 +34,8 @@ "calibrationMessage": "Dokładność pomiaru zależy od sprzętu urządzenia. Przetestuj aplikację i skalibruj wartości EV dla optymalnych wyników", "calibrationMessageCameraOnly": "Dokładność pomiaru zależy od kamery urządzenia. Przetestuj aplikację i skalibruj wartości EV dla optymalnych wyników", "camera": "Kamera", + "pinholeCamera": "Kamera otworkowa", + "equipmentProfileType": "Typ profilu sprzętu", "lightSensor": "Czujnik światła", "showEv100": "Pokaż EV\u2081\u2080\u2080", "meteringScreenLayout": "Układ ekranu pomiaru", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 1597201..dddde32 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -34,6 +34,8 @@ "calibrationMessage": "Точность измерений данного приложения полностью зависит от точности камеры и датчика освещенности вашего устройства. Поэтому рекомендуется самостоятельно подобрать калибровочные значения, которые дадут желаемый результат измерений.", "calibrationMessageCameraOnly": "Точность измерений данного приложения полностью зависит от точности камеры вашего устройства. Поэтому рекомендуется самостоятельно подобрать калибровочное значение, которое даст желаемый результат измерений.", "camera": "Камера", + "pinholeCamera": "Пинхол-камера", + "equipmentProfileType": "Тип профиля оборудования", "lightSensor": "Датчик освещённости", "showEv100": "Показывать EV\u2081\u2080\u2080", "meteringScreenLayout": "Элементы главного экрана", diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 9c069ee..c419b11 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -34,6 +34,8 @@ "calibrationMessage": "测量精度取决于设备硬件。请测试并校准 EV 值以获得最佳结果。", "calibrationMessageCameraOnly": "测量精度取决于设备摄像头。请测试并校准 EV 值以获得最佳结果。", "camera": "相机", + "pinholeCamera": "针孔相机", + "equipmentProfileType": "设备配置类型", "lightSensor": "光线传感器", "showEv100": "显示 EV\u2081\u2080\u2080", "meteringScreenLayout": "布局", diff --git a/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart index 6f60f81..9907bee 100644 --- a/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/bloc_equipment_profile_edit.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/screens/equipment_profile_edit/event_equipment_profile_edit.dart'; @@ -5,189 +6,199 @@ import 'package:lightmeter/screens/equipment_profile_edit/state_equipment_profil import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:uuid/uuid.dart'; -class EquipmentProfileEditBloc extends Bloc { +sealed class IEquipmentProfileEditBloc + extends Bloc, EquipmentProfileEditState> { + @protected final EquipmentProfilesProviderState profilesProvider; - final EquipmentProfile _originalEquipmentProfile; - EquipmentProfile _newEquipmentProfile; - final bool _isEdit; + @protected + final T originalEquipmentProfile; + @protected + final bool isEdit; - factory EquipmentProfileEditBloc( - EquipmentProfilesProviderState profilesProvider, { - required EquipmentProfile? profile, - required bool isEdit, - }) => - profile != null - ? EquipmentProfileEditBloc._( - profilesProvider, - profile, - isEdit, - ) - : EquipmentProfileEditBloc._( - profilesProvider, - EquipmentProfilesProvider.defaultProfile, - isEdit, - ); - - EquipmentProfileEditBloc._( - this.profilesProvider, - EquipmentProfile profile, - this._isEdit, - ) : _originalEquipmentProfile = profile, - _newEquipmentProfile = profile, + IEquipmentProfileEditBloc( + this.profilesProvider, { + required T profile, + required this.isEdit, + }) : originalEquipmentProfile = profile, super( - EquipmentProfileEditState( - name: profile.name, - apertureValues: profile.apertureValues, - shutterSpeedValues: profile.shutterSpeedValues, - isoValues: profile.isoValues, - ndValues: profile.ndValues, - lensZoom: profile.lensZoom, - exposureOffset: profile.exposureOffset, + EquipmentProfileEditState( + profile: profile, canSave: false, ), ) { - on( - (event, emit) async { - switch (event) { - case final EquipmentProfileNameChangedEvent e: - await _onNameChanged(e, emit); - case final EquipmentProfileApertureValuesChangedEvent e: - await _onApertureValuesChanged(e, emit); - case final EquipmentProfileShutterSpeedValuesChangedEvent e: - await _onShutterSpeedValuesChanged(e, emit); - case final EquipmentProfileIsoValuesChangedEvent e: - await _onIsoValuesChanged(e, emit); - case final EquipmentProfileNdValuesChangedEvent e: - await _onNdValuesChanged(e, emit); - case final EquipmentProfileLensZoomChangedEvent e: - await _onLensZoomChanged(e, emit); - case final EquipmentProfileExposureOffsetChangedEvent e: - await _onExposureOffsetChanged(e, emit); - case EquipmentProfileSaveEvent(): - await _onSave(event, emit); - case EquipmentProfileCopyEvent(): - await _onCopy(event, emit); - case EquipmentProfileDeleteEvent(): - await _onDelete(event, emit); - } - }, + on>(mapEventToState); + } + + @protected + @mustCallSuper + Future mapEventToState(IEquipmentProfileEditEvent event, Emitter emit) async { + switch (event) { + case EquipmentProfileSaveEvent(): + await _onSave(event, emit); + case EquipmentProfileCopyEvent(): + await _onCopy(event, emit); + case EquipmentProfileDeleteEvent(): + await _onDelete(event, emit); + default: + } + } + + @protected + Future createProfile(String id); + + @protected + void emitProfile(T profile, Emitter emit) { + emit( + state.copyWith( + profile: profile, + canSave: _canSave(profile), + ), + ); + } + + Future _onSave(EquipmentProfileSaveEvent _, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + final profileId = isEdit ? originalEquipmentProfile.id : const Uuid().v1(); + final newProfile = await createProfile(profileId); + assert(newProfile.id == profileId, 'The new profile id must be the same as the original profile id'); + await profilesProvider.addProfile(newProfile); + emit(state.copyWith(isLoading: false)); + } + + Future _onCopy(EquipmentProfileCopyEvent _, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + emit(state.copyWith(isLoading: false, profileToCopy: state.profile)); + } + + Future _onDelete(EquipmentProfileDeleteEvent _, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + await profilesProvider.deleteProfile(originalEquipmentProfile); + emit(state.copyWith(isLoading: false)); + } + + bool _canSave(T profile) => profile != originalEquipmentProfile; +} + +class EquipmentProfileEditBloc extends IEquipmentProfileEditBloc { + EquipmentProfileEditBloc( + super.profilesProvider, { + required super.profile, + required super.isEdit, + }); + + @override + Future mapEventToState(IEquipmentProfileEditEvent event, Emitter emit) async { + switch (event) { + case final EquipmentProfileNameChangedEvent e: + await _onNameChanged(e, emit); + case final EquipmentProfileApertureValuesChangedEvent e: + await _onApertureValuesChanged(e, emit); + case final EquipmentProfileShutterSpeedValuesChangedEvent e: + await _onShutterSpeedValuesChanged(e, emit); + case final EquipmentProfileIsoValuesChangedEvent e: + await _onIsoValuesChanged(e, emit); + case final EquipmentProfileNdValuesChangedEvent e: + await _onNdValuesChanged(e, emit); + case final EquipmentProfileLensZoomChangedEvent e: + await _onLensZoomChanged(e, emit); + case final EquipmentProfileExposureOffsetChangedEvent e: + await _onExposureOffsetChanged(e, emit); + default: + return super.mapEventToState(event, emit); + } + } + + @override + Future createProfile(String id) async { + return EquipmentProfile( + id: id, + name: state.profile.name, + apertureValues: state.profile.apertureValues, + shutterSpeedValues: state.profile.shutterSpeedValues, + isoValues: state.profile.isoValues, + ndValues: state.profile.ndValues, + lensZoom: state.profile.lensZoom, + exposureOffset: state.profile.exposureOffset, ); } Future _onNameChanged(EquipmentProfileNameChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(name: event.name); - emit( - state.copyWith( - name: event.name, - canSave: _canSave(event.name, state.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(name: event.name), emit); } Future _onApertureValuesChanged(EquipmentProfileApertureValuesChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(apertureValues: event.apertureValues); - emit( - state.copyWith( - apertureValues: event.apertureValues, - canSave: _canSave(state.name, state.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(apertureValues: event.apertureValues), emit); } Future _onShutterSpeedValuesChanged(EquipmentProfileShutterSpeedValuesChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(shutterSpeedValues: event.shutterSpeedValues); - emit( - state.copyWith( - shutterSpeedValues: event.shutterSpeedValues, - canSave: _canSave(state.name, state.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(shutterSpeedValues: event.shutterSpeedValues), emit); } Future _onIsoValuesChanged(EquipmentProfileIsoValuesChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(isoValues: event.isoValues); - emit( - state.copyWith( - isoValues: event.isoValues, - canSave: _canSave(state.name, state.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(isoValues: event.isoValues), emit); } Future _onNdValuesChanged(EquipmentProfileNdValuesChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(ndValues: event.ndValues); - emit( - state.copyWith( - ndValues: event.ndValues, - canSave: _canSave(state.name, state.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(ndValues: event.ndValues), emit); } Future _onLensZoomChanged(EquipmentProfileLensZoomChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(lensZoom: event.lensZoom); - emit( - state.copyWith( - lensZoom: event.lensZoom, - canSave: _canSave(state.name, event.lensZoom), - ), - ); + emitProfile(state.profile.copyWith(lensZoom: event.lensZoom), emit); } Future _onExposureOffsetChanged(EquipmentProfileExposureOffsetChangedEvent event, Emitter emit) async { - _newEquipmentProfile = _newEquipmentProfile.copyWith(exposureOffset: event.exposureOffset); - emit( - state.copyWith( - exposureOffset: event.exposureOffset, - canSave: _canSave(state.name, event.exposureOffset), - ), + emitProfile(state.profile.copyWith(exposureOffset: event.exposureOffset), emit); + } +} + +class PinholeEquipmentProfileEditBloc extends IEquipmentProfileEditBloc { + PinholeEquipmentProfileEditBloc( + super.profilesProvider, { + required super.profile, + required super.isEdit, + }); + + @override + Future mapEventToState(IEquipmentProfileEditEvent event, Emitter emit) async { + switch (event) { + case final EquipmentProfileNameChangedEvent e: + await _onNameChanged(e, emit); + case final EquipmentProfileApertureValuesChangedEvent e: + await _onApertureValuesChanged(e, emit); + case final EquipmentProfileLensZoomChangedEvent e: + await _onLensZoomChanged(e, emit); + case final EquipmentProfileExposureOffsetChangedEvent e: + await _onExposureOffsetChanged(e, emit); + default: + return super.mapEventToState(event, emit); + } + } + + @override + Future createProfile(String id) async { + return PinholeEquipmentProfile( + id: id, + name: state.profile.name, + aperture: state.profile.aperture, + isoValues: state.profile.isoValues, + lensZoom: state.profile.lensZoom, + exposureOffset: state.profile.exposureOffset, ); } - Future _onSave(EquipmentProfileSaveEvent _, Emitter emit) async { - emit(state.copyWith(isLoading: true)); - if (_isEdit) { - await profilesProvider.updateProfile( - EquipmentProfile( - id: _originalEquipmentProfile.id, - name: state.name, - apertureValues: state.apertureValues, - ndValues: state.ndValues, - shutterSpeedValues: state.shutterSpeedValues, - isoValues: state.isoValues, - lensZoom: state.lensZoom, - exposureOffset: state.exposureOffset, - ), - ); - } else { - await profilesProvider.addProfile( - EquipmentProfile( - id: const Uuid().v1(), - name: state.name, - apertureValues: state.apertureValues, - ndValues: state.ndValues, - shutterSpeedValues: state.shutterSpeedValues, - isoValues: state.isoValues, - lensZoom: state.lensZoom, - exposureOffset: state.exposureOffset, - ), - ); - } - emit(state.copyWith(isLoading: false)); + Future _onNameChanged(EquipmentProfileNameChangedEvent event, Emitter emit) async { + emitProfile(state.profile.copyWith(name: event.name), emit); } - Future _onCopy(EquipmentProfileCopyEvent _, Emitter emit) async { - emit(state.copyWith(isLoading: true)); - emit(state.copyWith(isLoading: false, profileToCopy: _originalEquipmentProfile)); + Future _onApertureValuesChanged(EquipmentProfileApertureValuesChangedEvent event, Emitter emit) async { + //emitProfile(state.profile.copyWith(apertureValues: event.apertureValues), emit); } - Future _onDelete(EquipmentProfileDeleteEvent _, Emitter emit) async { - emit(state.copyWith(isLoading: true)); - await profilesProvider.deleteProfile(_newEquipmentProfile); - emit(state.copyWith(isLoading: false)); + Future _onLensZoomChanged(EquipmentProfileLensZoomChangedEvent event, Emitter emit) async { + emitProfile(state.profile.copyWith(lensZoom: event.lensZoom), emit); } - bool _canSave(String name, double? lensZoom) { - return name.isNotEmpty && _newEquipmentProfile != _originalEquipmentProfile; + Future _onExposureOffsetChanged(EquipmentProfileExposureOffsetChangedEvent event, Emitter emit) async { + emitProfile(state.profile.copyWith(exposureOffset: event.exposureOffset), emit); } } diff --git a/lib/screens/equipment_profile_edit/event_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/event_equipment_profile_edit.dart index 8ad8309..691b745 100644 --- a/lib/screens/equipment_profile_edit/event_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/event_equipment_profile_edit.dart @@ -1,59 +1,59 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -sealed class EquipmentProfileEditEvent { - const EquipmentProfileEditEvent(); +sealed class IEquipmentProfileEditEvent { + const IEquipmentProfileEditEvent(); } -class EquipmentProfileNameChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileNameChangedEvent extends IEquipmentProfileEditEvent { final String name; const EquipmentProfileNameChangedEvent(this.name); } -class EquipmentProfileIsoValuesChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileIsoValuesChangedEvent extends IEquipmentProfileEditEvent { final List isoValues; const EquipmentProfileIsoValuesChangedEvent(this.isoValues); } -class EquipmentProfileNdValuesChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileNdValuesChangedEvent extends IEquipmentProfileEditEvent { final List ndValues; const EquipmentProfileNdValuesChangedEvent(this.ndValues); } -class EquipmentProfileApertureValuesChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileApertureValuesChangedEvent extends IEquipmentProfileEditEvent { final List apertureValues; const EquipmentProfileApertureValuesChangedEvent(this.apertureValues); } -class EquipmentProfileShutterSpeedValuesChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileShutterSpeedValuesChangedEvent extends IEquipmentProfileEditEvent { final List shutterSpeedValues; const EquipmentProfileShutterSpeedValuesChangedEvent(this.shutterSpeedValues); } -class EquipmentProfileLensZoomChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileLensZoomChangedEvent extends IEquipmentProfileEditEvent { final double lensZoom; const EquipmentProfileLensZoomChangedEvent(this.lensZoom); } -class EquipmentProfileExposureOffsetChangedEvent extends EquipmentProfileEditEvent { +class EquipmentProfileExposureOffsetChangedEvent extends IEquipmentProfileEditEvent { final double exposureOffset; const EquipmentProfileExposureOffsetChangedEvent(this.exposureOffset); } -class EquipmentProfileSaveEvent extends EquipmentProfileEditEvent { +class EquipmentProfileSaveEvent extends IEquipmentProfileEditEvent { const EquipmentProfileSaveEvent(); } -class EquipmentProfileCopyEvent extends EquipmentProfileEditEvent { +class EquipmentProfileCopyEvent extends IEquipmentProfileEditEvent { const EquipmentProfileCopyEvent(); } -class EquipmentProfileDeleteEvent extends EquipmentProfileEditEvent { +class EquipmentProfileDeleteEvent extends IEquipmentProfileEditEvent { const EquipmentProfileDeleteEvent(); } diff --git a/lib/screens/equipment_profile_edit/flow_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/flow_equipment_profile_edit.dart index 94db81a..be01402 100644 --- a/lib/screens/equipment_profile_edit/flow_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/flow_equipment_profile_edit.dart @@ -7,11 +7,14 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; enum EquipmentProfileEditType { add, edit } -class EquipmentProfileEditArgs { +class EquipmentProfileEditArgs { final EquipmentProfileEditType editType; - final EquipmentProfile? profile; + final T profile; - const EquipmentProfileEditArgs({required this.editType, this.profile}); + const EquipmentProfileEditArgs({ + required this.editType, + required this.profile, + }); } class EquipmentProfileEditFlow extends StatelessWidget { @@ -25,13 +28,50 @@ class EquipmentProfileEditFlow extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (_) => EquipmentProfileEditBloc( - EquipmentProfilesProvider.of(context), - profile: args.profile, - isEdit: _isEdit, + switch (args.profile) { + case final EquipmentProfile profile: + return _IEquipmentProfileBlocProvider( + create: (_) => EquipmentProfileEditBloc( + EquipmentProfilesProvider.of(context), + profile: profile, + isEdit: _isEdit, + ), + isEdit: _isEdit, + ); + case final PinholeEquipmentProfile profile: + return _IEquipmentProfileBlocProvider( + create: (_) => PinholeEquipmentProfileEditBloc( + EquipmentProfilesProvider.of(context), + profile: profile, + isEdit: _isEdit, + ), + isEdit: _isEdit, + ); + } + } +} + +class _IEquipmentProfileBlocProvider> + extends StatelessWidget { + final V Function(BuildContext context) create; + final bool isEdit; + + const _IEquipmentProfileBlocProvider({ + required this.create, + required this.isEdit, + super.key, + }); + + @override + Widget build(BuildContext context) { + return BlocProvider>( + create: create, + child: Builder( + builder: (context) => BlocProvider.value( + value: context.read>() as V, + child: EquipmentProfileEditScreen(isEdit: isEdit), + ), ), - child: EquipmentProfileEditScreen(isEdit: _isEdit), ); } } diff --git a/lib/screens/equipment_profile_edit/screen_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/screen_equipment_profile_edit.dart index f64a7e4..1eaa252 100644 --- a/lib/screens/equipment_profile_edit/screen_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/screen_equipment_profile_edit.dart @@ -17,7 +17,7 @@ import 'package:lightmeter/utils/double_to_zoom.dart'; import 'package:lightmeter/utils/to_string_signed.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -class EquipmentProfileEditScreen extends StatefulWidget { +class EquipmentProfileEditScreen extends StatefulWidget { final bool isEdit; const EquipmentProfileEditScreen({ @@ -26,13 +26,13 @@ class EquipmentProfileEditScreen extends StatefulWidget { }); @override - State createState() => _EquipmentProfileEditScreenState(); + State> createState() => _EquipmentProfileEditScreenState(); } -class _EquipmentProfileEditScreenState extends State { +class _EquipmentProfileEditScreenState extends State> { @override Widget build(BuildContext context) { - return BlocConsumer( + return BlocConsumer, EquipmentProfileEditState>( listenWhen: (previous, current) => previous.isLoading != current.isLoading, listener: (context, state) { if (state.isLoading) { @@ -41,9 +41,9 @@ class _EquipmentProfileEditScreenState extends State if (state.profileToCopy != null) { Navigator.of(context).pushReplacementNamed( NavigationRoutes.equipmentProfileEditScreen.name, - arguments: EquipmentProfileEditArgs( + arguments: EquipmentProfileEditArgs( editType: EquipmentProfileEditType.add, - profile: state.profileToCopy, + profile: state.profileToCopy!, ), ); } else { @@ -57,25 +57,25 @@ class _EquipmentProfileEditScreenState extends State child: SliverScreen( title: Text(widget.isEdit ? S.of(context).editEquipmentProfileTitle : S.of(context).addEquipmentProfileTitle), appBarActions: [ - BlocBuilder( + BlocBuilder, EquipmentProfileEditState>( buildWhen: (previous, current) => previous.canSave != current.canSave, builder: (context, state) => IconButton( onPressed: state.canSave ? () { - context.read().add(const EquipmentProfileSaveEvent()); + context.read>().add(const EquipmentProfileSaveEvent()); } : null, icon: const Icon(Icons.save_outlined), ), ), if (widget.isEdit) - BlocBuilder( + BlocBuilder, EquipmentProfileEditState>( buildWhen: (previous, current) => previous.canSave != current.canSave, builder: (context, state) => IconButton( onPressed: state.canSave ? null : () { - context.read().add(const EquipmentProfileCopyEvent()); + context.read>().add(const EquipmentProfileCopyEvent()); }, icon: const Icon(Icons.copy_outlined), ), @@ -83,7 +83,7 @@ class _EquipmentProfileEditScreenState extends State if (widget.isEdit) IconButton( onPressed: () { - context.read().add(const EquipmentProfileDeleteEvent()); + context.read>().add(const EquipmentProfileDeleteEvent()); }, icon: const Icon(Icons.delete_outlined), ), @@ -102,16 +102,25 @@ class _EquipmentProfileEditScreenState extends State padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), child: Opacity( opacity: state.isLoading ? Dimens.disabledOpacity : Dimens.enabledOpacity, - child: const Column( - children: [ - _NameFieldBuilder(), - _IsoValuesListTileBuilder(), - _NdValuesListTileBuilder(), - _ApertureValuesListTileBuilder(), - _ShutterSpeedValuesListTileBuilder(), - _LensZoomListTileBuilder(), - _ExposureOffsetListTileBuilder(), - ], + child: Column( + children: switch (state.profile) { + EquipmentProfile() => [ + _NameFieldBuilder(), + _ApertureValuesListTileBuilder(), + _ShutterSpeedValuesListTileBuilder(), + _IsoValuesListTileBuilder(), + _NdValuesListTileBuilder(), + _LensZoomListTileBuilder(), + _ExposureOffsetListTileBuilder(), + ], + PinholeEquipmentProfile() => [ + _NameFieldBuilder(), + // TODO: Add aperture value list tile for pinhole equipment profile + _IsoValuesListTileBuilder(), + _LensZoomListTileBuilder(), + _ExposureOffsetListTileBuilder(), + ], + }, ), ), ), @@ -126,12 +135,13 @@ class _EquipmentProfileEditScreenState extends State } } -class _NameFieldBuilder extends StatelessWidget { +class _NameFieldBuilder extends StatelessWidget { const _NameFieldBuilder(); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder, EquipmentProfileEditState>( + buildWhen: (previous, current) => previous.profile.name != current.profile.name, builder: (context, state) => Padding( padding: const EdgeInsets.only( left: Dimens.paddingM, @@ -141,13 +151,13 @@ class _NameFieldBuilder extends StatelessWidget { ), child: LightmeterTextField( autofocus: true, - initialValue: state.name, + initialValue: state.profile.name, maxLength: 48, hintText: S.of(context).name, style: Theme.of(context).listTileTheme.titleTextStyle, leading: const Icon(Icons.edit_outlined), onChanged: (value) { - context.read().add(EquipmentProfileNameChangedEvent(value)); + context.read>().add(EquipmentProfileNameChangedEvent(value)); }, ), ), @@ -155,20 +165,20 @@ class _NameFieldBuilder extends StatelessWidget { } } -class _IsoValuesListTileBuilder extends StatelessWidget { +class _IsoValuesListTileBuilder extends StatelessWidget { const _IsoValuesListTileBuilder(); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder, EquipmentProfileEditState>( builder: (context, state) => FilterListTile( icon: Icons.iso_outlined, title: S.of(context).isoValues, description: S.of(context).isoValuesFilterDescription, values: IsoValue.values, - selectedValues: state.isoValues, + selectedValues: state.profile.isoValues, onChanged: (value) { - context.read().add(EquipmentProfileIsoValuesChangedEvent(value)); + context.read>().add(EquipmentProfileIsoValuesChangedEvent(value)); }, ), ); @@ -180,13 +190,13 @@ class _NdValuesListTileBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder>( builder: (context, state) => FilterListTile( icon: Icons.filter_b_and_w_outlined, title: S.of(context).ndFilters, description: S.of(context).ndFiltersFilterDescription, values: NdValue.values, - selectedValues: state.ndValues, + selectedValues: state.profile.ndValues, onChanged: (value) { context.read().add(EquipmentProfileNdValuesChangedEvent(value)); }, @@ -200,13 +210,13 @@ class _ApertureValuesListTileBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder>( builder: (context, state) => RangePickerListTile( icon: Icons.camera_outlined, title: S.of(context).apertureValues, description: S.of(context).apertureValuesFilterDescription, values: ApertureValue.values, - selectedValues: state.apertureValues, + selectedValues: state.profile.apertureValues, onChanged: (value) { context.read().add(EquipmentProfileApertureValuesChangedEvent(value)); }, @@ -220,13 +230,13 @@ class _ShutterSpeedValuesListTileBuilder extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder>( builder: (context, state) => RangePickerListTile( icon: Icons.shutter_speed_outlined, title: S.of(context).shutterSpeedValues, description: S.of(context).shutterSpeedValuesFilterDescription, values: ShutterSpeedValue.values, - selectedValues: state.shutterSpeedValues, + selectedValues: state.profile.shutterSpeedValues, trailingAdapter: (context, value) => value.value == 1 ? S.of(context).shutterSpeedManualShort : value.toString(), dialogValueAdapter: (context, value) => value.value == 1 ? S.of(context).shutterSpeedManual : value.toString(), @@ -238,42 +248,44 @@ class _ShutterSpeedValuesListTileBuilder extends StatelessWidget { } } -class _LensZoomListTileBuilder extends StatelessWidget { +class _LensZoomListTileBuilder extends StatelessWidget { const _LensZoomListTileBuilder(); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder, EquipmentProfileEditState>( + buildWhen: (previous, current) => previous.profile.lensZoom != current.profile.lensZoom, builder: (context, state) => SliderPickerListTile( icon: Icons.zoom_in_outlined, title: S.of(context).lensZoom, description: S.of(context).lensZoomDescription, - value: state.lensZoom, + value: state.profile.lensZoom, range: CameraContainerBloc.zoomMaxRange, valueAdapter: (context, value) => value.toZoom(context), onChanged: (value) { - context.read().add(EquipmentProfileLensZoomChangedEvent(value)); + context.read>().add(EquipmentProfileLensZoomChangedEvent(value)); }, ), ); } } -class _ExposureOffsetListTileBuilder extends StatelessWidget { +class _ExposureOffsetListTileBuilder extends StatelessWidget { const _ExposureOffsetListTileBuilder(); @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder, EquipmentProfileEditState>( + buildWhen: (previous, current) => previous.profile.exposureOffset != current.profile.exposureOffset, builder: (context, state) => SliderPickerListTile( icon: Icons.light_mode_outlined, title: S.of(context).exposureOffset, description: S.of(context).exposureOffsetDescription, - value: state.exposureOffset, + value: state.profile.exposureOffset, range: CameraContainerBloc.exposureMaxRange, valueAdapter: (context, value) => S.of(context).evValue(value.toStringSignedAsFixed(1)), onChanged: (value) { - context.read().add(EquipmentProfileExposureOffsetChangedEvent(value)); + context.read>().add(EquipmentProfileExposureOffsetChangedEvent(value)); }, ), ); diff --git a/lib/screens/equipment_profile_edit/state_equipment_profile_edit.dart b/lib/screens/equipment_profile_edit/state_equipment_profile_edit.dart index 4727fbd..2018ec1 100644 --- a/lib/screens/equipment_profile_edit/state_equipment_profile_edit.dart +++ b/lib/screens/equipment_profile_edit/state_equipment_profile_edit.dart @@ -1,52 +1,28 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; -class EquipmentProfileEditState { - final String name; - final List apertureValues; - final List ndValues; - final List shutterSpeedValues; - final List isoValues; - final double lensZoom; - final double exposureOffset; +class EquipmentProfileEditState { + final T profile; + final T? profileToCopy; final bool canSave; final bool isLoading; - final EquipmentProfile? profileToCopy; const EquipmentProfileEditState({ - required this.name, - required this.apertureValues, - required this.ndValues, - required this.shutterSpeedValues, - required this.isoValues, - required this.lensZoom, - required this.exposureOffset, + required this.profile, required this.canSave, this.isLoading = false, this.profileToCopy, }); - EquipmentProfileEditState copyWith({ - String? name, - List? apertureValues, - List? ndValues, - List? shutterSpeedValues, - List? isoValues, - double? lensZoom, - double? exposureOffset, + EquipmentProfileEditState copyWith({ + T? profile, + T? profileToCopy, bool? canSave, bool? isLoading, - EquipmentProfile? profileToCopy, }) => - EquipmentProfileEditState( - name: name ?? this.name, - apertureValues: apertureValues ?? this.apertureValues, - ndValues: ndValues ?? this.ndValues, - shutterSpeedValues: shutterSpeedValues ?? this.shutterSpeedValues, - isoValues: isoValues ?? this.isoValues, - lensZoom: lensZoom ?? this.lensZoom, - exposureOffset: exposureOffset ?? this.exposureOffset, + EquipmentProfileEditState( + profile: profile ?? this.profile, + profileToCopy: profileToCopy ?? this.profileToCopy, canSave: canSave ?? this.canSave, isLoading: isLoading ?? this.isLoading, - profileToCopy: profileToCopy ?? this.profileToCopy, ); } diff --git a/lib/screens/equipment_profiles/screen_equipment_profiles.dart b/lib/screens/equipment_profiles/screen_equipment_profiles.dart index 4fb1afa..99e5f93 100644 --- a/lib/screens/equipment_profiles/screen_equipment_profiles.dart +++ b/lib/screens/equipment_profiles/screen_equipment_profiles.dart @@ -4,11 +4,14 @@ import 'package:lightmeter/navigation/routes.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/equipment_profile_edit/flow_equipment_profile_edit.dart'; +import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; import 'package:lightmeter/screens/shared/sliver_placeholder/widget_sliver_placeholder.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; import 'package:lightmeter/utils/guard_pro_tap.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; +enum _EquipmentProfileType { regular, pinhole } + class EquipmentProfilesScreen extends StatefulWidget { const EquipmentProfilesScreen({super.key}); @@ -45,15 +48,45 @@ class _EquipmentProfilesScreenState extends State with guardProTap( context, () { - Navigator.of(context).pushNamed( - NavigationRoutes.equipmentProfileEditScreen.name, - arguments: const EquipmentProfileEditArgs(editType: EquipmentProfileEditType.add), - ); + showDialog<_EquipmentProfileType>( + context: context, + builder: (_) => DialogPicker<_EquipmentProfileType>( + icon: Icons.camera_alt_outlined, + title: S.of(context).equipmentProfileType, + selectedValue: _EquipmentProfileType.regular, + values: _EquipmentProfileType.values, + titleAdapter: (context, value) => switch (value) { + _EquipmentProfileType.regular => S.of(context).camera, + _EquipmentProfileType.pinhole => S.of(context).pinholeCamera, + }, + ), + ).then((value) { + if (value != null && mounted) { + Navigator.of(context).pushNamed( + NavigationRoutes.equipmentProfileEditScreen.name, + arguments: switch (value) { + _EquipmentProfileType.regular => const EquipmentProfileEditArgs( + editType: EquipmentProfileEditType.add, + profile: EquipmentProfilesProvider.defaultProfile, + ), + _EquipmentProfileType.pinhole => EquipmentProfileEditArgs( + editType: EquipmentProfileEditType.add, + profile: PinholeEquipmentProfile( + id: EquipmentProfilesProvider.defaultProfile.id, + name: EquipmentProfilesProvider.defaultProfile.name, + aperture: 22, + isoValues: EquipmentProfilesProvider.defaultProfile.isoValues, + ), + ), + }, + ); + } + }); }, ); } - void _editProfile(EquipmentProfile profile) { + void _editProfile(IEquipmentProfile profile) { Navigator.of(context).pushNamed( NavigationRoutes.equipmentProfileEditScreen.name, arguments: EquipmentProfileEditArgs( @@ -65,9 +98,9 @@ class _EquipmentProfilesScreenState extends State with } class _EquipmentProfilesListBuilder extends StatelessWidget { - final List values; - final void Function(EquipmentProfile profile) onEdit; - final void Function(EquipmentProfile profile, bool value) onCheckbox; + final List values; + final void Function(IEquipmentProfile profile) onEdit; + final void Function(String id, bool value) onCheckbox; const _EquipmentProfilesListBuilder({ required this.values, @@ -102,7 +135,7 @@ class _EquipmentProfilesListBuilder extends StatelessWidget { title: Text(values[index].name), controlAffinity: ListTileControlAffinity.leading, value: EquipmentProfiles.inUseOf(context).contains(values[index]), - onChanged: (value) => onCheckbox(values[index], value ?? false), + onChanged: (value) => onCheckbox(values[index].id, value ?? false), secondary: IconButton( onPressed: () => onEdit(values[index]), icon: const Icon(Icons.edit_outlined),