From be0617a99c00bf199c106c5723c03db451ca2966 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sat, 1 Apr 2023 22:04:55 +0300 Subject: [PATCH] ML-46 Add reciprocity failure formulas for some films (#47) * added `Film` model with reciprocity formulas * added `FeaturesConfig` * added film picker * unused import * get ISO and ND from equipment profile * udpate iso on film changed * typo --- lib/data/models/film.dart | 228 ++++++++++++++++++ lib/data/shared_prefs_service.dart | 8 + lib/features.dart | 3 + lib/l10n/intl_en.arb | 1 + lib/l10n/intl_fr.arb | 1 + lib/l10n/intl_ru.arb | 1 + lib/res/dimens.dart | 2 +- lib/screens/metering/bloc_metering.dart | 32 ++- .../provider_container_camera.dart | 10 +- .../widget_container_camera.dart | 17 +- .../provider_container_light_sensor.dart | 10 +- .../widget_container_light_sensor.dart | 13 +- .../widget_container_readings.dart | 77 ++++-- lib/screens/metering/event_metering.dart | 7 + lib/screens/metering/screen_metering.dart | 11 + lib/screens/metering/state_metering.dart | 11 +- .../widget_settings_section_metering.dart | 3 +- 17 files changed, 381 insertions(+), 54 deletions(-) create mode 100644 lib/data/models/film.dart create mode 100644 lib/features.dart diff --git a/lib/data/models/film.dart b/lib/data/models/film.dart new file mode 100644 index 0000000..8b789ce --- /dev/null +++ b/lib/data/models/film.dart @@ -0,0 +1,228 @@ +import 'dart:math'; + +import 'package:flutter/foundation.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; + +double log10(double x) => log(x) / log(10); + +double log10polynomian( + double x, + double a, + double b, + double c, +) => + a * pow(log10(x), 2) + b * log10(x) + c; + +/// Only Ilford films have reciprocity formulas provided by the manufacturer: +/// https://www.ilfordphoto.com/wp/wp-content/uploads/2017/06/Reciprocity-Failure-Compensation.pdf +/// +/// Reciprocity formulas for Fomapan films and Kodak films are from here: +/// https://www.flickr.com/groups/86738082@N00/discuss/72157626050157470/ +/// +/// Cinema films like Kodak 5222/7222 Double-X and respective CineStill films (cause they are basically a modification of Kodak) +/// do not have any reciprocity failure information, as these films are ment to be used in cinema +/// with appropriate light and pretty short shutter speeds. +/// +class Film { + final String name; + final int iso; + + const Film(this.name, this.iso); + + const Film.other() + : name = '', + iso = 0; + + @override + String toString() => name; + + ShutterSpeedValue reciprocityFailure(ShutterSpeedValue shutterSpeed) { + if (shutterSpeed.isFraction) { + return shutterSpeed; + } else { + return ShutterSpeedValue( + reciprocityFormula(shutterSpeed.rawValue), + shutterSpeed.isFraction, + shutterSpeed.stopType, + ); + } + } + + @protected + double reciprocityFormula(double t) => t; + + static const List values = [ + Film.other(), + FomapanFilm.creative100(), + FomapanFilm.creative200(), + FomapanFilm.action400(), + IlfordFilm.ortho(), + IlfordFilm.delta100(), + IlfordFilm.delta400(), + IlfordFilm.delta3200(), + IlfordFilm.fp4(), + IlfordFilm.hp5(), + IlfordFilm.panf(), + IlfordFilm.sfx200(), + IlfordFilm.xp2super(), + IlfordFilm.pan100(), + IlfordFilm.pan400(), + KodakFilm.tmax100(), + KodakFilm.tmax400(), + KodakFilm.tmax3200(), + KodakFilm.trix320(), + KodakFilm.trix400(), + ]; +} + +/// https://www.tate.org.uk/documents/598/page_6_7_agfa_stocks_0.pdf +/// https://www.filmwasters.com/forum/index.php?topic=5298.0 +// {{1,1.87},{2,3.73},{3,8.06},{4,13.93},{5,21.28},{6,23.00},{7,30.12},{8,38.05},{9,44.75},{10,50.12},{20,117},{30,202},{40,293},{50,413},{60,547},{70,694},{80,853},{90,1022},{100,1202}}; +class AgfaFilm extends Film { + final double a; + final double b; + final double c; + + const AgfaFilm.apx100() + : a = 1, + b = 5, + c = 2, + super('Agfa APX 100', 100); + + const AgfaFilm.apx400() + : a = 1.5, + b = 4.5, + c = 3, + super('Agfa APX 400', 400); + + @override + double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c); +} + +class FomapanFilm extends Film { + final double a; + final double b; + final double c; + + /// https://www.foma.cz/en/fomapan-100 + const FomapanFilm.creative100() + : a = 1, + b = 5, + c = 2, + super('Fomapan CREATIVE 100', 100); + + /// https://www.foma.cz/en/fomapan-200 + const FomapanFilm.creative200() + : a = 1.5, + b = 4.5, + c = 3, + super('Fomapan CREATIVE 200', 400); + + /// https://www.foma.cz/en/fomapan-100 + const FomapanFilm.action400() + : a = -1.25, + b = 5.75, + c = 1.5, + super('Fomapan ACTION 400', 400); +} + +class IlfordFilm extends Film { + final double reciprocityPower; + + /// https://www.ilfordphoto.com/amfile/file/download/file/1948/product/1650/ + const IlfordFilm.ortho() + : reciprocityPower = 1.25, + super('Ilford ORTHO+', 80); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1919/product/686/ + const IlfordFilm.fp4() + : reciprocityPower = 1.26, + super('Ilford FP4+', 125); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1903/product/691/ + const IlfordFilm.hp5() + : reciprocityPower = 1.31, + super('Ilford HP5+', 400); + + /// https://www.ilfordphoto.com/amfile/file/download/file/3/product/679/ + const IlfordFilm.delta100() + : reciprocityPower = 1.26, + super('Ilford DELTA 100', 100); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1915/product/684/ + const IlfordFilm.delta400() + : reciprocityPower = 1.41, + super('Ilford DELTA 400', 400); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1913/product/682/ + const IlfordFilm.delta3200() + : reciprocityPower = 1.33, + super('Ilford DELTA 3200', 3200); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1905/product/699/ + const IlfordFilm.panf() + : reciprocityPower = 1.33, + super('Ilford Pan F+', 50); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1907/product/701/ + const IlfordFilm.sfx200() + : reciprocityPower = 1.31, + super('Ilford SFX 200', 200); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1909/product/703/ + const IlfordFilm.xp2super() + : reciprocityPower = 1.31, + super('Ilford XP2 SUPER', 400); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1958/product/696/ + const IlfordFilm.pan100() + : reciprocityPower = 1.26, + super('Kentemere 100', 100); + + /// https://www.ilfordphoto.com/amfile/file/download/file/1959/product/697/ + const IlfordFilm.pan400() + : reciprocityPower = 1.30, + super('Kentemere 400', 100); + + @override + double reciprocityFormula(double t) => pow(t, reciprocityPower).toDouble(); +} + +class KodakFilm extends Film { + final double a; + final double b; + final double c; + + const KodakFilm.tmax100() + : a = 1 / 6, + b = 0, + c = 4 / 3, + super('Kodak T-MAX 100', 100); + + const KodakFilm.tmax400() + : a = 2 / 3, + b = -1 / 2, + c = 4 / 3, + super('Kodak T-MAX 400', 400); + + const KodakFilm.tmax3200() + : a = 7 / 6, + b = -1, + c = 4 / 3, + super('Kodak T-MAX 3200', 3200); + + const KodakFilm.trix320() + : a = 2, + b = 1, + c = 2, + super('Kodak TRI-X 320', 320); + + const KodakFilm.trix400() + : a = 2, + b = 1, + c = 2, + super('Kodak TRI-X 400', 400); + + @override + double reciprocityFormula(double t) => t * log10polynomian(t, a, b, c); +} diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index ab8f115..c5054fb 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -4,6 +4,7 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'models/ev_source_type.dart'; +import 'models/film.dart'; import 'models/theme_type.dart'; class UserPreferencesService { @@ -13,6 +14,7 @@ class UserPreferencesService { static const _evSourceTypeKey = "evSourceType"; static const _cameraEvCalibrationKey = "cameraEvCalibration"; static const _lightSensorEvCalibrationKey = "lightSensorEvCalibration"; + static const _filmKey = "film"; static const _caffeineKey = "caffeine"; static const _hapticsKey = "haptics"; @@ -105,6 +107,12 @@ class UserPreferencesService { bool get dynamicColor => _sharedPreferences.getBool(_dynamicColorKey) ?? false; set dynamicColor(bool value) => _sharedPreferences.setBool(_dynamicColorKey, value); + Film get film => Film.values.firstWhere( + (e) => e.name == _sharedPreferences.getString(_filmKey), + orElse: () => Film.values.first, + ); + set film(Film value) => _sharedPreferences.setString(_filmKey, value.name); + String get selectedEquipmentProfileId => ''; set selectedEquipmentProfileId(String id) {} diff --git a/lib/features.dart b/lib/features.dart new file mode 100644 index 0000000..deede30 --- /dev/null +++ b/lib/features.dart @@ -0,0 +1,3 @@ +class FeaturesConfig { + static const bool equipmentProfilesEnabled = false; +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 5a6d06e..4a2b65a 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -34,6 +34,7 @@ "calibrationMessageCameraOnly": "The accuracy of the readings measured by this application depends entirely on the rear camera of the device. Therefore, consider testing this application and setting up an EV calibration value that will give you the desired measurement results.", "camera": "Camera", "lightSensor": "Light sensor", + "film": "Film", "equipment": "Equipment", "equipmentProfileName": "Equipment profile name", "equipmentProfileNameHint": "Praktica MTL5B", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 7e19d56..49debfb 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -34,6 +34,7 @@ "calibrationMessageCameraOnly": "La précision des lectures mesurées par cette application dépend entièrement de la caméra arrière de l'appareil. Par conséquent, envisagez de tester cette application et de configurer une valeur d'étalonnage EV qui vous donnera les résultats de mesure souhaités.", "camera": "Caméra", "lightSensor": "Capteur de lumière", + "film": "Pellicule", "equipment": "Équipement", "equipmentProfileName": "Nom du profil de l'équipement", "equipmentProfileNameHint": "Praktica MTL5B", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index f6d201f..dece4f7 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -34,6 +34,7 @@ "calibrationMessageCameraOnly": "Точность измерений данного приложения полностью зависит от точности камеры вашего устройства. Поэтому рекомендуется самостоятельно подобрать калибровочное значение, которое даст желаемый результат измерений.", "camera": "Камера", "lightSensor": "Датчик освещённости", + "film": "Пленка", "equipment": "Оборудование", "equipmentProfileName": "Название профиля", "equipmentProfileNameHint": "Praktica MTL5B", diff --git a/lib/res/dimens.dart b/lib/res/dimens.dart index ce931e2..61d79b5 100644 --- a/lib/res/dimens.dart +++ b/lib/res/dimens.dart @@ -32,7 +32,7 @@ class Dimens { // TopBar /// Probably this is a bad practice, but with text size locked, the height is always 212 static const double readingContainerSingleValueHeight = 76; - static const double readingContainerDefaultHeight = 212; + static const double readingContainerDefaultHeight = 288; // `CenteredSlider` static const double cameraSliderTrackHeight = grid4; diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 332bace..8f3dc98 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' @@ -31,6 +32,7 @@ class MeteringBloc extends Bloc { late IsoValue _iso = _userPreferencesService.iso; late NdValue _nd = _userPreferencesService.ndFilter; + late Film _film = _userPreferencesService.film; double _ev = 0.0; bool _isMeteringInProgress = false; @@ -42,10 +44,11 @@ class MeteringBloc extends Bloc { this.stopType, ) : super( MeteringEndedState( - iso: _userPreferencesService.iso, ev: 0.0, + film: _userPreferencesService.film, + iso: _userPreferencesService.iso, nd: _userPreferencesService.ndFilter, - exposurePairs: [], + exposurePairs: const [], ), ) { _communicationSubscription = _communicationBloc.stream @@ -55,6 +58,7 @@ class MeteringBloc extends Bloc { on(_onEquipmentProfileChanged); on(_onStopTypeChanged); + on(_onFilmChanged); on(_onIsoChanged); on(_onNdChanged); on(_onMeasure); @@ -100,7 +104,23 @@ class MeteringBloc extends Bloc { _emitMeasuredState(emit); } + void _onFilmChanged(FilmChangedEvent event, Emitter emit) { + if (_iso.value != event.data.iso) { + final newIso = isoValues.firstWhere( + (e) => e.value == event.data.iso, + orElse: () => _iso, + ); + add(IsoChangedEvent(newIso)); + } + _film = event.data; + _userPreferencesService.film = event.data; + _emitMeasuredState(emit); + } + void _onIsoChanged(IsoChangedEvent event, Emitter emit) { + if (event.isoValue.value != _film.iso) { + _film = Film.values.first; + } _userPreferencesService.iso = event.isoValue; _ev = _ev + log2(event.isoValue.value / _iso.value); _iso = event.isoValue; @@ -130,14 +150,16 @@ class MeteringBloc extends Bloc { void _emitMeasuredState(Emitter emit) { emit(_isMeteringInProgress ? MeteringInProgressState( - iso: _iso, ev: _ev, + film: _film, + iso: _iso, nd: _nd, exposurePairs: _buildExposureValues(_ev), ) : MeteringEndedState( - iso: _iso, ev: _ev, + film: _film, + iso: _iso, nd: _nd, exposurePairs: _buildExposureValues(_ev), )); @@ -188,7 +210,7 @@ class MeteringBloc extends Bloc { itemsCount, (index) => ExposurePair( _apertureValues[index + apertureOffset], - _shutterSpeedValues[index + shutterSpeedOffset], + _film.reciprocityFailure(_shutterSpeedValues[index + shutterSpeedOffset]), ), growable: false, ); diff --git a/lib/screens/metering/components/camera_container/provider_container_camera.dart b/lib/screens/metering/components/camera_container/provider_container_camera.dart index 82957e1..c2643f2 100644 --- a/lib/screens/metering/components/camera_container/provider_container_camera.dart +++ b/lib/screens/metering/components/camera_container/provider_container_camera.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; -import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -12,8 +12,10 @@ import 'widget_container_camera.dart'; class CameraContainerProvider extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; + final Film film; final IsoValue iso; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -21,8 +23,10 @@ class CameraContainerProvider extends StatelessWidget { const CameraContainerProvider({ required this.fastest, required this.slowest, + required this.film, required this.iso, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -40,10 +44,10 @@ class CameraContainerProvider extends StatelessWidget { child: CameraContainer( fastest: fastest, slowest: slowest, - isoValues: EquipmentProfile.of(context).isoValues, + film: film, iso: iso, - ndValues: EquipmentProfile.of(context).ndValues, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, diff --git a/lib/screens/metering/components/camera_container/widget_container_camera.dart b/lib/screens/metering/components/camera_container/widget_container_camera.dart index 8aacb4e..a9cf631 100644 --- a/lib/screens/metering/components/camera_container/widget_container_camera.dart +++ b/lib/screens/metering/components/camera_container/widget_container_camera.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; +import 'package:lightmeter/features.dart'; import 'package:lightmeter/platform_config.dart'; -import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart'; import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; @@ -21,10 +22,10 @@ import 'state_container_camera.dart'; class CameraContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final List isoValues; + final Film film; final IsoValue iso; - final List ndValues; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -32,10 +33,10 @@ class CameraContainer extends StatelessWidget { const CameraContainer({ required this.fastest, required this.slowest, - required this.isoValues, + required this.film, required this.iso, - required this.ndValues, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -49,7 +50,7 @@ class CameraContainer extends StatelessWidget { PlatformConfig.cameraPreviewAspectRatio; double topBarOverflow = Dimens.readingContainerDefaultHeight - cameraViewHeight; - if (EquipmentProfiles.of(context).isNotEmpty) { + if (FeaturesConfig.equipmentProfilesEnabled) { topBarOverflow += Dimens.readingContainerSingleValueHeight; topBarOverflow += Dimens.paddingS; } @@ -60,10 +61,10 @@ class CameraContainer extends StatelessWidget { readingsContainer: ReadingsContainer( fastest: fastest, slowest: slowest, - isoValues: isoValues, + film: film, iso: iso, - ndValues: ndValues, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, ), diff --git a/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart index bab0c7b..71bf5e6 100644 --- a/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/interactors/metering_interactor.dart'; -import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -12,8 +12,10 @@ import 'widget_container_light_sensor.dart'; class LightSensorContainerProvider extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; + final Film film; final IsoValue iso; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -21,8 +23,10 @@ class LightSensorContainerProvider extends StatelessWidget { const LightSensorContainerProvider({ required this.fastest, required this.slowest, + required this.film, required this.iso, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -40,10 +44,10 @@ class LightSensorContainerProvider extends StatelessWidget { child: LightSensorContainer( fastest: fastest, slowest: slowest, - isoValues: EquipmentProfile.of(context).isoValues, + film: film, iso: iso, - ndValues: EquipmentProfile.of(context).ndValues, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, diff --git a/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart index 34018ac..4380f02 100644 --- a/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/widget_container_light_sensor.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart'; import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart'; @@ -9,10 +10,10 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class LightSensorContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final List isoValues; + final Film film; final IsoValue iso; - final List ndValues; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -20,10 +21,10 @@ class LightSensorContainer extends StatelessWidget { const LightSensorContainer({ required this.fastest, required this.slowest, - required this.isoValues, + required this.film, required this.iso, - required this.ndValues, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -38,10 +39,10 @@ class LightSensorContainer extends StatelessWidget { readingsContainer: ReadingsContainer( fastest: fastest, slowest: slowest, - isoValues: isoValues, + film: film, iso: iso, - ndValues: ndValues, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, ), diff --git a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart index 88b86f9..92902b8 100644 --- a/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart +++ b/lib/screens/metering/components/shared/readings_container/widget_container_readings.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; +import 'package:lightmeter/features.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/res/dimens.dart'; @@ -12,20 +14,20 @@ import 'components/reading_value_container/widget_container_reading_value.dart'; class ReadingsContainer extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; - final List isoValues; + final Film film; final IsoValue iso; - final List ndValues; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; const ReadingsContainer({ required this.fastest, required this.slowest, - required this.isoValues, + required this.film, required this.iso, - required this.ndValues, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, super.key, @@ -36,12 +38,8 @@ class ReadingsContainer extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (EquipmentProfiles.of(context).isNotEmpty) ...[ - _EquipmentProfilePicker( - selectedValue: EquipmentProfile.of(context), - values: EquipmentProfiles.of(context), - onChanged: EquipmentProfileProvider.of(context).setProfile, - ), + if (FeaturesConfig.equipmentProfilesEnabled) ...[ + const _EquipmentProfilePicker(), const _InnerPadding(), ], ReadingValueContainer( @@ -57,12 +55,18 @@ class ReadingsContainer extends StatelessWidget { ], ), const _InnerPadding(), + _FilmPicker( + values: Film.values, + selectedValue: film, + onChanged: onFilmChanged, + ), + const _InnerPadding(), Row( children: [ Expanded( child: _IsoValuePicker( selectedValue: iso, - values: isoValues, + values: EquipmentProfile.of(context).isoValues, onChanged: onIsoChanged, ), ), @@ -70,7 +74,7 @@ class ReadingsContainer extends StatelessWidget { Expanded( child: _NdValuePicker( selectedValue: nd, - values: ndValues, + values: EquipmentProfile.of(context).ndValues, onChanged: onNdChanged, ), ), @@ -86,28 +90,51 @@ class _InnerPadding extends SizedBox { } class _EquipmentProfilePicker extends StatelessWidget { - final List values; - final EquipmentProfileData selectedValue; - final ValueChanged onChanged; - - const _EquipmentProfilePicker({ - required this.selectedValue, - required this.values, - required this.onChanged, - }); + const _EquipmentProfilePicker(); @override Widget build(BuildContext context) { return AnimatedDialogPicker( title: S.of(context).equipmentProfile, - selectedValue: selectedValue, - values: values, + selectedValue: EquipmentProfile.of(context), + values: EquipmentProfiles.of(context), itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), - onChanged: onChanged, + onChanged: EquipmentProfileProvider.of(context).setProfile, closedChild: ReadingValueContainer.singleValue( value: ReadingValue( label: S.of(context).equipmentProfile, - value: selectedValue.id.isEmpty ? S.of(context).none : selectedValue.name, + value: EquipmentProfile.of(context).id.isEmpty + ? S.of(context).none + : EquipmentProfile.of(context).name, + ), + ), + ); + } +} + +class _FilmPicker extends StatelessWidget { + final List values; + final Film selectedValue; + final ValueChanged onChanged; + + const _FilmPicker({ + required this.values, + required this.selectedValue, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + return AnimatedDialogPicker( + title: S.of(context).film, + selectedValue: selectedValue, + values: values, + itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name), + onChanged: onChanged, + closedChild: ReadingValueContainer.singleValue( + value: ReadingValue( + label: S.of(context).film, + value: selectedValue.name.isEmpty ? S.of(context).none : selectedValue.name, ), ), ); diff --git a/lib/screens/metering/event_metering.dart b/lib/screens/metering/event_metering.dart index 95166dd..6a9abc6 100644 --- a/lib/screens/metering/event_metering.dart +++ b/lib/screens/metering/event_metering.dart @@ -1,3 +1,4 @@ +import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; abstract class MeteringEvent { @@ -16,6 +17,12 @@ class EquipmentProfileChangedEvent extends MeteringEvent { const EquipmentProfileChangedEvent(this.equipmentProfileData); } +class FilmChangedEvent extends MeteringEvent { + final Film data; + + const FilmChangedEvent(this.data); +} + class IsoChangedEvent extends MeteringEvent { final IsoValue isoValue; diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 5b8d8e1..f9ac3ad 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/environment.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; import 'package:lightmeter/providers/ev_source_type_provider.dart'; @@ -44,8 +45,10 @@ class _MeteringScreenState extends State { ? _MeteringContainerBuidler( fastest: state.fastest, slowest: state.slowest, + film: state.film, iso: state.iso, nd: state.nd, + onFilmChanged: (value) => _bloc.add(FilmChangedEvent(value)), onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)), onNdChanged: (value) => _bloc.add(NdChangedEvent(value)), exposurePairs: state.exposurePairs, @@ -73,8 +76,10 @@ class _MeteringScreenState extends State { class _MeteringContainerBuidler extends StatelessWidget { final ExposurePair? fastest; final ExposurePair? slowest; + final Film film; final IsoValue iso; final NdValue nd; + final ValueChanged onFilmChanged; final ValueChanged onIsoChanged; final ValueChanged onNdChanged; final List exposurePairs; @@ -82,8 +87,10 @@ class _MeteringContainerBuidler extends StatelessWidget { const _MeteringContainerBuidler({ required this.fastest, required this.slowest, + required this.film, required this.iso, required this.nd, + required this.onFilmChanged, required this.onIsoChanged, required this.onNdChanged, required this.exposurePairs, @@ -95,8 +102,10 @@ class _MeteringContainerBuidler extends StatelessWidget { ? CameraContainerProvider( fastest: fastest, slowest: slowest, + film: film, iso: iso, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, @@ -104,8 +113,10 @@ class _MeteringContainerBuidler extends StatelessWidget { : LightSensorContainerProvider( fastest: fastest, slowest: slowest, + film: film, iso: iso, nd: nd, + onFilmChanged: onFilmChanged, onIsoChanged: onIsoChanged, onNdChanged: onNdChanged, exposurePairs: exposurePairs, diff --git a/lib/screens/metering/state_metering.dart b/lib/screens/metering/state_metering.dart index 7a103b8..7016374 100644 --- a/lib/screens/metering/state_metering.dart +++ b/lib/screens/metering/state_metering.dart @@ -1,6 +1,9 @@ +import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/film.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; +@immutable abstract class MeteringState { const MeteringState(); } @@ -11,12 +14,14 @@ class LoadingState extends MeteringState { abstract class MeteringDataState extends MeteringState { final double ev; + final Film film; final IsoValue iso; final NdValue nd; final List exposurePairs; const MeteringDataState({ required this.ev, + required this.film, required this.iso, required this.nd, required this.exposurePairs, @@ -27,8 +32,9 @@ abstract class MeteringDataState extends MeteringState { } class MeteringInProgressState extends MeteringDataState { - MeteringInProgressState({ + const MeteringInProgressState({ required super.ev, + required super.film, required super.iso, required super.nd, required super.exposurePairs, @@ -36,8 +42,9 @@ class MeteringInProgressState extends MeteringDataState { } class MeteringEndedState extends MeteringDataState { - MeteringEndedState({ + const MeteringEndedState({ required super.ev, + required super.film, required super.iso, required super.nd, required super.exposurePairs, diff --git a/lib/screens/settings/components/metering/widget_settings_section_metering.dart b/lib/screens/settings/components/metering/widget_settings_section_metering.dart index db30e84..45e057a 100644 --- a/lib/screens/settings/components/metering/widget_settings_section_metering.dart +++ b/lib/screens/settings/components/metering/widget_settings_section_metering.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/features.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; @@ -16,7 +17,7 @@ class MeteringSettingsSection extends StatelessWidget { children: const [ StopTypeListTile(), CalibrationListTile(), - EquipmentProfilesListTile(), + if (FeaturesConfig.equipmentProfilesEnabled) EquipmentProfilesListTile(), ], ); }