fixed extreme exposure pairs reciprocity display

This commit is contained in:
Vadim 2023-09-13 22:00:42 +02:00
parent e9376e67a0
commit eeb70bfaff
10 changed files with 218 additions and 163 deletions

View file

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class EquipmentProfilePicker extends StatelessWidget {
const EquipmentProfilePicker();
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<EquipmentProfile>(
icon: Icons.camera,
title: S.of(context).equipmentProfile,
selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.of(context),
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
onChanged: EquipmentProfileProvider.of(context).setProfile,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).equipmentProfile,
value: EquipmentProfiles.selectedOf(context).id.isEmpty
? S.of(context).none
: EquipmentProfiles.selectedOf(context).name,
),
),
);
}
}

View file

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/exposure_pair.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
class ExtremeExposurePairsContainer extends StatelessWidget {
final ExposurePair? fastest;
final ExposurePair? slowest;
const ExtremeExposurePairsContainer({
required this.fastest,
required this.slowest,
super.key,
});
@override
Widget build(BuildContext context) {
return ReadingValueContainer(
values: [
ReadingValue(
label: S.of(context).fastestExposurePair,
value: _exposurePairToString(context, fastest),
),
ReadingValue(
label: S.of(context).slowestExposurePair,
value: _exposurePairToString(context, slowest),
),
],
);
}
String _exposurePairToString(BuildContext context, ExposurePair? pair) {
if (pair == null) {
return '-';
}
return '${pair.aperture} - ${Films.selectedOf(context).reciprocityFailure(pair.shutterSpeed)}';
}
}

View file

@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class FilmPicker extends StatelessWidget {
final IsoValue selectedIso;
const FilmPicker({required this.selectedIso});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<Film>(
icon: Icons.camera_roll,
title: S.of(context).film,
selectedValue: Films.selectedOf(context),
values: Films.of(context),
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name),
onChanged: FilmsProvider.of(context).setFilm,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: _label(context),
value: Films.selectedOf(context).name.isEmpty
? S.of(context).none
: Films.selectedOf(context).name,
),
),
);
}
String _label(BuildContext context) {
if (Films.selectedOf(context) == const Film.other() ||
Films.selectedOf(context).iso == selectedIso.value) {
return S.of(context).film;
}
final evDiff = IsoValue(
Films.selectedOf(context).iso,
StopType.full,
).difference(selectedIso);
if (evDiff > 0) {
return S.of(context).filmPush;
} else if (evDiff < 0) {
return S.of(context).filmPull;
} else {
return S.of(context).film;
}
}
}

View file

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class IsoValuePicker extends StatelessWidget {
final List<IsoValue> values;
final IsoValue selectedValue;
final ValueChanged<IsoValue> onChanged;
const IsoValuePicker({
required this.selectedValue,
required this.values,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<IsoValue>(
icon: Icons.iso,
title: S.of(context).iso,
subtitle: S.of(context).filmSpeed,
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(value.value.toString()),
// using ascending order, because increase in film speed rises EV
itemTrailingBuilder: (selected, value) => value.value != selected.value
? Text(S.of(context).evValue(selected.toStringDifference(value)))
: null,
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).iso,
value: selectedValue.value.toString(),
),
),
);
}
}

View file

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/widget_picker_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/reading_value_container/widget_container_reading_value.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class NdValuePicker extends StatelessWidget {
final List<NdValue> values;
final NdValue selectedValue;
final ValueChanged<NdValue> onChanged;
const NdValuePicker({
required this.selectedValue,
required this.values,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<NdValue>(
icon: Icons.filter_b_and_w,
title: S.of(context).nd,
subtitle: S.of(context).ndFilterFactor,
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(
value.value == 0 ? S.of(context).none : value.value.toString(),
),
// using descending order, because ND filter darkens image & lowers EV
itemTrailingBuilder: (selected, value) => value.value != selected.value
? Text(S.of(context).evValue(value.toStringDifference(selected)))
: null,
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).nd,
value: selectedValue.value.toString(),
),
),
);
}
}

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/animated_dialog/widget_dialog_animated.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart';
// Has to be stateful, so that [GlobalKey] is not recreated. // Has to be stateful, so that [GlobalKey] is not recreated.
// Otherwise use will no be able to close the dialog after EV value has changed. // Otherwise use will no be able to close the dialog after EV value has changed.

View file

@ -1,11 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/exposure_pair.dart';
import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/providers/user_preferences_provider.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart';
import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/animated_dialog_picker/widget_picker_dialog_animated.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/reading_value_container/widget_container_reading_value.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -36,24 +38,16 @@ class ReadingsContainer extends StatelessWidget {
context, context,
MeteringScreenLayoutFeature.equipmentProfiles, MeteringScreenLayoutFeature.equipmentProfiles,
)) ...[ )) ...[
const _EquipmentProfilePicker(), const EquipmentProfilePicker(),
const _InnerPadding(), const _InnerPadding(),
], ],
if (UserPreferencesProvider.meteringScreenFeatureOf( if (UserPreferencesProvider.meteringScreenFeatureOf(
context, context,
MeteringScreenLayoutFeature.extremeExposurePairs, MeteringScreenLayoutFeature.extremeExposurePairs,
)) ...[ )) ...[
ReadingValueContainer( ExtremeExposurePairsContainer(
values: [ fastest: fastest,
ReadingValue( slowest: slowest,
label: S.of(context).fastestExposurePair,
value: fastest != null ? fastest!.toString() : '-',
),
ReadingValue(
label: S.of(context).slowestExposurePair,
value: fastest != null ? slowest!.toString() : '-',
),
],
), ),
const _InnerPadding(), const _InnerPadding(),
], ],
@ -61,13 +55,13 @@ class ReadingsContainer extends StatelessWidget {
context, context,
MeteringScreenLayoutFeature.filmPicker, MeteringScreenLayoutFeature.filmPicker,
)) ...[ )) ...[
_FilmPicker(selectedIso: iso), FilmPicker(selectedIso: iso),
const _InnerPadding(), const _InnerPadding(),
], ],
Row( Row(
children: [ children: [
Expanded( Expanded(
child: _IsoValuePicker( child: IsoValuePicker(
selectedValue: iso, selectedValue: iso,
values: EquipmentProfiles.selectedOf(context).isoValues, values: EquipmentProfiles.selectedOf(context).isoValues,
onChanged: onIsoChanged, onChanged: onIsoChanged,
@ -75,7 +69,7 @@ class ReadingsContainer extends StatelessWidget {
), ),
const _InnerPadding(), const _InnerPadding(),
Expanded( Expanded(
child: _NdValuePicker( child: NdValuePicker(
selectedValue: nd, selectedValue: nd,
values: EquipmentProfiles.selectedOf(context).ndValues, values: EquipmentProfiles.selectedOf(context).ndValues,
onChanged: onNdChanged, onChanged: onNdChanged,
@ -91,145 +85,3 @@ class ReadingsContainer extends StatelessWidget {
class _InnerPadding extends SizedBox { class _InnerPadding extends SizedBox {
const _InnerPadding() : super(height: Dimens.grid8, width: Dimens.grid8); const _InnerPadding() : super(height: Dimens.grid8, width: Dimens.grid8);
} }
class _EquipmentProfilePicker extends StatelessWidget {
const _EquipmentProfilePicker();
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<EquipmentProfile>(
icon: Icons.camera,
title: S.of(context).equipmentProfile,
selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.of(context),
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
onChanged: EquipmentProfileProvider.of(context).setProfile,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).equipmentProfile,
value: EquipmentProfiles.selectedOf(context).id.isEmpty
? S.of(context).none
: EquipmentProfiles.selectedOf(context).name,
),
),
);
}
}
class _FilmPicker extends StatelessWidget {
final IsoValue selectedIso;
const _FilmPicker({required this.selectedIso});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<Film>(
icon: Icons.camera_roll,
title: S.of(context).film,
selectedValue: Films.selectedOf(context),
values: Films.of(context),
itemTitleBuilder: (_, value) => Text(value.name.isEmpty ? S.of(context).none : value.name),
onChanged: FilmsProvider.of(context).setFilm,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: _label(context),
value: Films.selectedOf(context).name.isEmpty
? S.of(context).none
: Films.selectedOf(context).name,
),
),
);
}
String _label(BuildContext context) {
if (Films.selectedOf(context) == const Film.other() ||
Films.selectedOf(context).iso == selectedIso.value) {
return S.of(context).film;
}
final evDiff = IsoValue(
Films.selectedOf(context).iso,
StopType.full,
).difference(selectedIso);
if (evDiff > 0) {
return S.of(context).filmPush;
} else if (evDiff < 0) {
return S.of(context).filmPull;
} else {
return S.of(context).film;
}
}
}
class _IsoValuePicker extends StatelessWidget {
final List<IsoValue> values;
final IsoValue selectedValue;
final ValueChanged<IsoValue> onChanged;
const _IsoValuePicker({
required this.selectedValue,
required this.values,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<IsoValue>(
icon: Icons.iso,
title: S.of(context).iso,
subtitle: S.of(context).filmSpeed,
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(value.value.toString()),
// using ascending order, because increase in film speed rises EV
itemTrailingBuilder: (selected, value) => value.value != selected.value
? Text(S.of(context).evValue(selected.toStringDifference(value)))
: null,
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).iso,
value: selectedValue.value.toString(),
),
),
);
}
}
class _NdValuePicker extends StatelessWidget {
final List<NdValue> values;
final NdValue selectedValue;
final ValueChanged<NdValue> onChanged;
const _NdValuePicker({
required this.selectedValue,
required this.values,
required this.onChanged,
});
@override
Widget build(BuildContext context) {
return AnimatedDialogPicker<NdValue>(
icon: Icons.filter_b_and_w,
title: S.of(context).nd,
subtitle: S.of(context).ndFilterFactor,
selectedValue: selectedValue,
values: values,
itemTitleBuilder: (_, value) => Text(
value.value == 0 ? S.of(context).none : value.value.toString(),
),
// using descending order, because ND filter darkens image & lowers EV
itemTrailingBuilder: (selected, value) => value.value != selected.value
? Text(S.of(context).evValue(value.toStringDifference(selected)))
: null,
onChanged: onChanged,
closedChild: ReadingValueContainer.singleValue(
value: ReadingValue(
label: S.of(context).nd,
value: selectedValue.value.toString(),
),
),
);
}
}