mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-21 15:00:40 +00:00
ML-170 Show long shutter speeds for all selected aperture values (#172)
* generate exposures > 1" * fixed unit tests * added manual shutter speed to equipment profiles * fixed integration tests * fixed unit tests * fixed long exposures overflow * migrated to resources 1.2.0 and iap 0.10.0 * removed unnecessary loop * fixed extreme exposure pairs test * updated master screenshots * fixed iap stub
This commit is contained in:
parent
ec1f1eeeb4
commit
bc7e6e14d0
20 changed files with 345 additions and 869 deletions
|
@ -13,7 +13,7 @@ dependencies:
|
||||||
m3_lightmeter_resources:
|
m3_lightmeter_resources:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
||||||
ref: main
|
ref: v1.2.0
|
||||||
shared_preferences: 2.2.0
|
shared_preferences: 2.2.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
|
@ -44,7 +44,7 @@ void testE2E(String description) {
|
||||||
testWidgets(
|
testWidgets(
|
||||||
description,
|
description,
|
||||||
(tester) async {
|
(tester) async {
|
||||||
await tester.pumpApplication(equipmentProfiles: [], films: []);
|
await tester.pumpApplication(equipmentProfiles: [], filmsInUse: []);
|
||||||
|
|
||||||
/// Create Praktica + Zenitar profile from scratch
|
/// Create Praktica + Zenitar profile from scratch
|
||||||
await tester.openSettings();
|
await tester.openSettings();
|
||||||
|
@ -60,7 +60,7 @@ void testE2E(String description) {
|
||||||
await tester.setZoomValue(0, mockEquipmentProfiles[0].lensZoom);
|
await tester.setZoomValue(0, mockEquipmentProfiles[0].lensZoom);
|
||||||
expect(find.text('x1.91'), findsOneWidget);
|
expect(find.text('x1.91'), findsOneWidget);
|
||||||
expect(find.text('f/1.7 - f/16'), findsOneWidget);
|
expect(find.text('f/1.7 - f/16'), findsOneWidget);
|
||||||
expect(find.text('1/1000 - 16"'), findsOneWidget);
|
expect(find.text('1/1000 - B'), findsOneWidget);
|
||||||
|
|
||||||
/// Create Praktica + Jupiter profile from Zenitar profile
|
/// Create Praktica + Jupiter profile from Zenitar profile
|
||||||
await tester.tap(find.byIcon(Icons.copy).first);
|
await tester.tap(find.byIcon(Icons.copy).first);
|
||||||
|
@ -71,7 +71,7 @@ void testE2E(String description) {
|
||||||
await tester.setZoomValue(1, mockEquipmentProfiles[1].lensZoom);
|
await tester.setZoomValue(1, mockEquipmentProfiles[1].lensZoom);
|
||||||
expect(find.text('x5.02'), findsOneWidget);
|
expect(find.text('x5.02'), findsOneWidget);
|
||||||
expect(find.text('f/3.5 - f/22'), findsOneWidget);
|
expect(find.text('f/3.5 - f/22'), findsOneWidget);
|
||||||
expect(find.text('1/1000 - 16"'), findsNWidgets(2));
|
expect(find.text('1/1000 - B'), findsNWidgets(2));
|
||||||
await tester.navigatorPop();
|
await tester.navigatorPop();
|
||||||
|
|
||||||
/// Select some films
|
/// Select some films
|
||||||
|
|
|
@ -10,18 +10,21 @@ class _MockIAPStorageService extends Mock implements IAPStorageService {}
|
||||||
class MockIAPProviders extends StatefulWidget {
|
class MockIAPProviders extends StatefulWidget {
|
||||||
final List<EquipmentProfile>? equipmentProfiles;
|
final List<EquipmentProfile>? equipmentProfiles;
|
||||||
final String selectedEquipmentProfileId;
|
final String selectedEquipmentProfileId;
|
||||||
final List<Film>? films;
|
final List<Film> availableFilms;
|
||||||
|
final List<Film> filmsInUse;
|
||||||
final Film selectedFilm;
|
final Film selectedFilm;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const MockIAPProviders({
|
const MockIAPProviders({
|
||||||
this.equipmentProfiles = const [],
|
this.equipmentProfiles = const [],
|
||||||
this.selectedEquipmentProfileId = '',
|
this.selectedEquipmentProfileId = '',
|
||||||
this.films = mockFilms,
|
List<Film>? availableFilms,
|
||||||
|
List<Film>? filmsInUse,
|
||||||
this.selectedFilm = const Film.other(),
|
this.selectedFilm = const Film.other(),
|
||||||
required this.child,
|
required this.child,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
}) : availableFilms = availableFilms ?? mockFilms,
|
||||||
|
filmsInUse = filmsInUse ?? mockFilms;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MockIAPProviders> createState() => _MockIAPProvidersState();
|
State<MockIAPProviders> createState() => _MockIAPProvidersState();
|
||||||
|
@ -36,7 +39,7 @@ class _MockIAPProvidersState extends State<MockIAPProviders> {
|
||||||
mockIAPStorageService = _MockIAPStorageService();
|
mockIAPStorageService = _MockIAPStorageService();
|
||||||
when(() => mockIAPStorageService.equipmentProfiles).thenReturn(widget.equipmentProfiles ?? mockEquipmentProfiles);
|
when(() => mockIAPStorageService.equipmentProfiles).thenReturn(widget.equipmentProfiles ?? mockEquipmentProfiles);
|
||||||
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId);
|
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId);
|
||||||
when(() => mockIAPStorageService.filmsInUse).thenReturn(widget.films ?? mockFilms);
|
when(() => mockIAPStorageService.filmsInUse).thenReturn(widget.filmsInUse);
|
||||||
when(() => mockIAPStorageService.selectedFilm).thenReturn(widget.selectedFilm);
|
when(() => mockIAPStorageService.selectedFilm).thenReturn(widget.selectedFilm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,7 @@ class _MockIAPProvidersState extends State<MockIAPProviders> {
|
||||||
storageService: mockIAPStorageService,
|
storageService: mockIAPStorageService,
|
||||||
child: FilmsProvider(
|
child: FilmsProvider(
|
||||||
storageService: mockIAPStorageService,
|
storageService: mockIAPStorageService,
|
||||||
availableFilms: widget.films ?? mockFilms,
|
availableFilms: widget.availableFilms,
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -78,7 +81,7 @@ final mockEquipmentProfiles = [
|
||||||
],
|
],
|
||||||
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(16, false, StopType.full)) + 1,
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1, false, StopType.full)) + 1,
|
||||||
),
|
),
|
||||||
isoValues: const [
|
isoValues: const [
|
||||||
IsoValue(50, StopType.full),
|
IsoValue(50, StopType.full),
|
||||||
|
@ -108,7 +111,7 @@ final mockEquipmentProfiles = [
|
||||||
],
|
],
|
||||||
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(16, false, StopType.full)) + 1,
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1, false, StopType.full)) + 1,
|
||||||
),
|
),
|
||||||
isoValues: const [
|
isoValues: const [
|
||||||
IsoValue(50, StopType.full),
|
IsoValue(50, StopType.full),
|
||||||
|
|
|
@ -22,7 +22,8 @@ extension WidgetTesterCommonActions on WidgetTester {
|
||||||
IAPProductStatus productStatus = IAPProductStatus.purchased,
|
IAPProductStatus productStatus = IAPProductStatus.purchased,
|
||||||
List<EquipmentProfile>? equipmentProfiles,
|
List<EquipmentProfile>? equipmentProfiles,
|
||||||
String selectedEquipmentProfileId = '',
|
String selectedEquipmentProfileId = '',
|
||||||
List<Film>? films,
|
List<Film>? availableFilms,
|
||||||
|
List<Film>? filmsInUse,
|
||||||
Film selectedFilm = const Film.other(),
|
Film selectedFilm = const Film.other(),
|
||||||
}) async {
|
}) async {
|
||||||
await pumpWidget(
|
await pumpWidget(
|
||||||
|
@ -33,7 +34,8 @@ extension WidgetTesterCommonActions on WidgetTester {
|
||||||
child: MockIAPProviders(
|
child: MockIAPProviders(
|
||||||
equipmentProfiles: equipmentProfiles,
|
equipmentProfiles: equipmentProfiles,
|
||||||
selectedEquipmentProfileId: selectedEquipmentProfileId,
|
selectedEquipmentProfileId: selectedEquipmentProfileId,
|
||||||
films: films,
|
availableFilms: availableFilms,
|
||||||
|
filmsInUse: filmsInUse,
|
||||||
selectedFilm: selectedFilm,
|
selectedFilm: selectedFilm,
|
||||||
child: const Application(),
|
child: const Application(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
"ndFiltersFilterDescription": "Select the ND filters to display. These may be your most commonly used ND filters or the ones that fit your lens.",
|
"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",
|
"shutterSpeedValues": "Shutter speed values",
|
||||||
"shutterSpeedValuesFilterDescription": "Select the range of shutter speed values to display. This is usually determined by the camera body you are using.",
|
"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",
|
||||||
"isoValues": "ISO values",
|
"isoValues": "ISO values",
|
||||||
"isoValuesFilterDescription": "Select the ISO values to display. These may be your most commonly used values or those supported by your camera.",
|
"isoValuesFilterDescription": "Select the ISO values to display. These may be your most commonly used values or those supported by your camera.",
|
||||||
"lensZoom": "Lens zoom",
|
"lensZoom": "Lens zoom",
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
"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.",
|
"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",
|
"shutterSpeedValues": "Valeurs 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.",
|
"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",
|
||||||
"isoValues": "Valeurs ISO",
|
"isoValues": "Valeurs ISO",
|
||||||
"isoValuesFilterDescription": "Sélectionnez les valeurs ISO à afficher. Ce sont peut-être vos valeurs les plus couramment utilisées ou celles prises en charge par votre caméra.",
|
"isoValuesFilterDescription": "Sélectionnez les valeurs ISO à afficher. Ce sont peut-être vos valeurs les plus couramment utilisées ou celles prises en charge par votre caméra.",
|
||||||
"lensZoom": "Zoom sur l'objectif",
|
"lensZoom": "Zoom sur l'objectif",
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
"ndFiltersFilterDescription": "Выберите ND фильтры для отображения. Это могут быть наиболее часто используемые ND фильтры или фильтры, подходящие под ваш объектив.",
|
"ndFiltersFilterDescription": "Выберите ND фильтры для отображения. Это могут быть наиболее часто используемые ND фильтры или фильтры, подходящие под ваш объектив.",
|
||||||
"shutterSpeedValues": "Значения выдержки",
|
"shutterSpeedValues": "Значения выдержки",
|
||||||
"shutterSpeedValuesFilterDescription": "Выберите диапазон значений выдержки. Обычно ограничивается возможностями вашей камеры.",
|
"shutterSpeedValuesFilterDescription": "Выберите диапазон значений выдержки. Обычно ограничивается возможностями вашей камеры.",
|
||||||
|
"shutterSpeedManualShort": "B",
|
||||||
|
"shutterSpeedManual": "Ручная",
|
||||||
"isoValues": "Значения ISO",
|
"isoValues": "Значения ISO",
|
||||||
"isoValuesFilterDescription": "Выберите значения ISO для отображения. Это может быть наиболее часто используемые значения или значения, поддерживаемые вашей камерой.",
|
"isoValuesFilterDescription": "Выберите значения ISO для отображения. Это может быть наиболее часто используемые значения или значения, поддерживаемые вашей камерой.",
|
||||||
"lensZoom": "Зум объектива",
|
"lensZoom": "Зум объектива",
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
"ndFiltersFilterDescription": "选择要显示的 ND 滤镜系数。可能是您最常用的 ND 滤镜,也可能是适合您镜头的减光镜。",
|
"ndFiltersFilterDescription": "选择要显示的 ND 滤镜系数。可能是您最常用的 ND 滤镜,也可能是适合您镜头的减光镜。",
|
||||||
"shutterSpeedValues": "快门速度",
|
"shutterSpeedValues": "快门速度",
|
||||||
"shutterSpeedValuesFilterDescription": "选择要显示的快门速度范围。这通常由您使用的相机机身决定。",
|
"shutterSpeedValuesFilterDescription": "选择要显示的快门速度范围。这通常由您使用的相机机身决定。",
|
||||||
|
"shutterSpeedManualShort": "B",
|
||||||
|
"shutterSpeedManual": "手册",
|
||||||
"isoValues": "ISO",
|
"isoValues": "ISO",
|
||||||
"isoValuesFilterDescription": "选择要显示的 ISO 。这些可能是您常用的ISO值,也可以是相机支持的ISO范围。",
|
"isoValuesFilterDescription": "选择要显示的 ISO 。这些可能是您常用的ISO值,也可以是相机支持的ISO范围。",
|
||||||
"lensZoom": "镜头变焦",
|
"lensZoom": "镜头变焦",
|
||||||
|
|
|
@ -15,9 +15,13 @@ class ExposurePairsListItem<T extends PhotographyStopValue> extends StatelessWid
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> rowChildren = [
|
final List<Widget> rowChildren = [
|
||||||
Text(
|
Flexible(
|
||||||
value.toString(),
|
child: Text(
|
||||||
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onBackground),
|
value.toString(),
|
||||||
|
style: labelTextStyle(context).copyWith(color: Theme.of(context).colorScheme.onBackground),
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: Dimens.grid8),
|
const SizedBox(width: Dimens.grid8),
|
||||||
ColoredBox(
|
ColoredBox(
|
||||||
|
|
|
@ -158,22 +158,40 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
shutterSpeedOffset = 0;
|
shutterSpeedOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int itemsCount = min(
|
int itemsCount = min(
|
||||||
apertureValues.length + shutterSpeedOffset,
|
apertureValues.length + shutterSpeedOffset,
|
||||||
shutterSpeedValues.length + apertureOffset,
|
shutterSpeedValues.length + apertureOffset,
|
||||||
) -
|
) -
|
||||||
max(apertureOffset, shutterSpeedOffset);
|
max(apertureOffset, shutterSpeedOffset);
|
||||||
|
|
||||||
if (itemsCount <= 0) {
|
if (apertureOffset == apertureValues.length) {
|
||||||
return List.empty();
|
return List.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final lastPreCalcShutterSpeed =
|
||||||
|
shutterSpeedValues.elementAtOrNull(itemsCount - 1 + shutterSpeedOffset) ?? shutterSpeedValues.last;
|
||||||
|
final preCalculatedItemsCount = itemsCount;
|
||||||
|
if (itemsCount <= 0) {
|
||||||
|
itemsCount = apertureValues.length;
|
||||||
|
} else {
|
||||||
|
itemsCount += (apertureValues.length - 1) - (itemsCount - 1 + apertureOffset);
|
||||||
|
}
|
||||||
|
|
||||||
final exposurePairs = List.generate(
|
final exposurePairs = List.generate(
|
||||||
itemsCount,
|
itemsCount,
|
||||||
(index) => ExposurePair(
|
(index) {
|
||||||
apertureValues[index + apertureOffset],
|
final stopDifference = (index - (preCalculatedItemsCount - 1)) / (stopType.index + 1);
|
||||||
shutterSpeedValues[index + shutterSpeedOffset],
|
final newShutterSpeed = log2(lastPreCalcShutterSpeed.rawValue) + stopDifference;
|
||||||
),
|
return ExposurePair(
|
||||||
|
apertureValues[index + apertureOffset],
|
||||||
|
shutterSpeedValues.elementAtOrNull(index + shutterSpeedOffset) ??
|
||||||
|
ShutterSpeedValue(
|
||||||
|
calcShutterSpeed(newShutterSpeed),
|
||||||
|
false,
|
||||||
|
stopDifference == stopDifference.roundToDouble() ? StopType.full : stopType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
growable: false,
|
growable: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -191,7 +209,9 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
);
|
);
|
||||||
final endCutEV = max(
|
final endCutEV = max(
|
||||||
equipmentApertureValues.last.difference(exposurePairs.last.aperture),
|
equipmentApertureValues.last.difference(exposurePairs.last.aperture),
|
||||||
equipmentShutterSpeedValues.last.difference(exposurePairs.last.shutterSpeed),
|
equipmentShutterSpeedValues.last != ShutterSpeedValue.values.last
|
||||||
|
? equipmentShutterSpeedValues.last.difference(exposurePairs.last.shutterSpeed)
|
||||||
|
: double.negativeInfinity,
|
||||||
);
|
);
|
||||||
|
|
||||||
final startCut = (startCutEV * (stopType.index + 1)).round().clamp(0, itemsCount);
|
final startCut = (startCutEV * (stopType.index + 1)).round().clamp(0, itemsCount);
|
||||||
|
@ -203,3 +223,12 @@ class MeteringContainerBuidler extends StatelessWidget {
|
||||||
return exposurePairs.sublist(startCut, itemsCount - endCut);
|
return exposurePairs.sublist(startCut, itemsCount - endCut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double calcShutterSpeed(double stopValue) {
|
||||||
|
final shutterSpeed = pow(2, stopValue);
|
||||||
|
if (stopValue < 1.5) {
|
||||||
|
return (shutterSpeed * 10).round() / 10;
|
||||||
|
} else {
|
||||||
|
return shutterSpeed.roundToDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ class RangePickerListTile<T extends PhotographyValue> extends StatelessWidget {
|
||||||
final String description;
|
final String description;
|
||||||
final List<T> selectedValues;
|
final List<T> selectedValues;
|
||||||
final List<T> values;
|
final List<T> values;
|
||||||
|
final String Function(BuildContext context, T value)? trailingAdapter;
|
||||||
|
final String Function(BuildContext context, T value)? dialogValueAdapter;
|
||||||
final ValueChanged<List<T>> onChanged;
|
final ValueChanged<List<T>> onChanged;
|
||||||
|
|
||||||
const RangePickerListTile({
|
const RangePickerListTile({
|
||||||
|
@ -16,6 +18,8 @@ class RangePickerListTile<T extends PhotographyValue> extends StatelessWidget {
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.selectedValues,
|
required this.selectedValues,
|
||||||
required this.values,
|
required this.values,
|
||||||
|
this.trailingAdapter,
|
||||||
|
this.dialogValueAdapter,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
@ -25,7 +29,7 @@ class RangePickerListTile<T extends PhotographyValue> extends StatelessWidget {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: Icon(icon),
|
leading: Icon(icon),
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
trailing: Text("${selectedValues.first} - ${selectedValues.last}"),
|
trailing: Text(_trailing(context)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showDialog<List<T>>(
|
showDialog<List<T>>(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -35,7 +39,7 @@ class RangePickerListTile<T extends PhotographyValue> extends StatelessWidget {
|
||||||
description: description,
|
description: description,
|
||||||
values: values,
|
values: values,
|
||||||
selectedValues: selectedValues,
|
selectedValues: selectedValues,
|
||||||
titleAdapter: (_, value) => value.toString(),
|
valueAdapter: (context, value) => dialogValueAdapter?.call(context, value) ?? value.toString(),
|
||||||
),
|
),
|
||||||
).then((values) {
|
).then((values) {
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
|
@ -45,4 +49,16 @@ class RangePickerListTile<T extends PhotographyValue> extends StatelessWidget {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _trailing(BuildContext context) {
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
buffer.write(trailingAdapter?.call(context, selectedValues.first) ?? selectedValues.first);
|
||||||
|
if (selectedValues.first != selectedValues.last) {
|
||||||
|
buffer.writeAll([
|
||||||
|
' - ',
|
||||||
|
trailingAdapter?.call(context, selectedValues.last) ?? selectedValues.last,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,10 @@ class _AnimatedEquipmentListTiles extends AnimatedWidget {
|
||||||
values: ShutterSpeedValue.values,
|
values: ShutterSpeedValue.values,
|
||||||
selectedValues: equipmentData.shutterSpeedValues,
|
selectedValues: equipmentData.shutterSpeedValues,
|
||||||
onChanged: onShutterSpeedValuesSelected,
|
onChanged: onShutterSpeedValuesSelected,
|
||||||
|
trailingAdapter: (context, value) =>
|
||||||
|
value.value == 1 ? S.of(context).shutterSpeedManualShort : value.toString(),
|
||||||
|
dialogValueAdapter: (context, value) =>
|
||||||
|
value.value == 1 ? S.of(context).shutterSpeedManual : value.toString(),
|
||||||
),
|
),
|
||||||
SliderPickerListTile(
|
SliderPickerListTile(
|
||||||
icon: Icons.zoom_in,
|
icon: Icons.zoom_in,
|
||||||
|
|
|
@ -9,7 +9,7 @@ class DialogRangePicker<T extends PhotographyValue> extends StatefulWidget {
|
||||||
final String description;
|
final String description;
|
||||||
final List<T> values;
|
final List<T> values;
|
||||||
final List<T> selectedValues;
|
final List<T> selectedValues;
|
||||||
final String Function(BuildContext context, T value) titleAdapter;
|
final String Function(BuildContext context, T value) valueAdapter;
|
||||||
|
|
||||||
const DialogRangePicker({
|
const DialogRangePicker({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
@ -17,7 +17,7 @@ class DialogRangePicker<T extends PhotographyValue> extends StatefulWidget {
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.values,
|
required this.values,
|
||||||
required this.selectedValues,
|
required this.selectedValues,
|
||||||
required this.titleAdapter,
|
required this.valueAdapter,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ class _DialogRangePickerState<T extends PhotographyValue> extends State<DialogRa
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(widget.values[_start].toString()),
|
Text(widget.valueAdapter(context, widget.values[_start])),
|
||||||
Text(widget.values[_end].toString()),
|
Text(widget.valueAdapter(context, widget.values[_end])),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -29,11 +29,11 @@ dependencies:
|
||||||
m3_lightmeter_iap:
|
m3_lightmeter_iap:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
url: "https://github.com/vodemn/m3_lightmeter_iap"
|
||||||
ref: v0.9.2
|
ref: v0.10.0
|
||||||
m3_lightmeter_resources:
|
m3_lightmeter_resources:
|
||||||
git:
|
git:
|
||||||
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
url: "https://github.com/vodemn/m3_lightmeter_resources"
|
||||||
ref: main
|
ref: v1.2.0
|
||||||
material_color_utilities: 0.5.0
|
material_color_utilities: 0.5.0
|
||||||
package_info_plus: 4.2.0
|
package_info_plus: 4.2.0
|
||||||
permission_handler: 10.4.3
|
permission_handler: 10.4.3
|
||||||
|
|
|
@ -72,7 +72,8 @@ void main() {
|
||||||
testWidgets('Generate light theme screenshots', (tester) async {
|
testWidgets('Generate light theme screenshots', (tester) async {
|
||||||
mockSharedPrefs(ThemeType.light, lightThemeColor);
|
mockSharedPrefs(ThemeType.light, lightThemeColor);
|
||||||
await tester.pumpApplication(
|
await tester.pumpApplication(
|
||||||
films: [_mockFilm],
|
availableFilms: [_mockFilm],
|
||||||
|
filmsInUse: [_mockFilm],
|
||||||
selectedFilm: _mockFilm,
|
selectedFilm: _mockFilm,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -115,7 +116,8 @@ void main() {
|
||||||
(tester) async {
|
(tester) async {
|
||||||
mockSharedPrefs(ThemeType.dark, darkThemeColor);
|
mockSharedPrefs(ThemeType.dark, darkThemeColor);
|
||||||
await tester.pumpApplication(
|
await tester.pumpApplication(
|
||||||
films: [_mockFilm],
|
availableFilms: [_mockFilm],
|
||||||
|
filmsInUse: [_mockFilm],
|
||||||
selectedFilm: _mockFilm,
|
selectedFilm: _mockFilm,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ class _GoldenTestApplicationMockState extends State<GoldenTestApplicationMock> {
|
||||||
child: MockIAPProviders(
|
child: MockIAPProviders(
|
||||||
equipmentProfiles: mockEquipmentProfiles,
|
equipmentProfiles: mockEquipmentProfiles,
|
||||||
selectedEquipmentProfileId: mockEquipmentProfiles.first.id,
|
selectedEquipmentProfileId: mockEquipmentProfiles.first.id,
|
||||||
films: films,
|
selectedFilm: mockFilms.first,
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
|
|
@ -91,7 +91,7 @@ final _mockEquipmentProfiles = [
|
||||||
ndValues: NdValue.values.sublist(0, 3),
|
ndValues: NdValue.values.sublist(0, 3),
|
||||||
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
shutterSpeedValues: ShutterSpeedValue.values.sublist(
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)),
|
||||||
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(16, false, StopType.full)) + 1,
|
ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1, false, StopType.full)) + 1,
|
||||||
),
|
),
|
||||||
isoValues: const [
|
isoValues: const [
|
||||||
IsoValue(50, StopType.full),
|
IsoValue(50, StopType.full),
|
||||||
|
|
|
@ -38,7 +38,7 @@ void main() {
|
||||||
expect(find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), findsOneWidget);
|
expect(find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), findsOneWidget);
|
||||||
expect(find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), findsOneWidget);
|
expect(find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), findsOneWidget);
|
||||||
expect(find.descendant(of: pickerFinder, matching: find.text('f/1.0 - 1/2000')), findsOneWidget);
|
expect(find.descendant(of: pickerFinder, matching: find.text('f/1.0 - 1/2000')), findsOneWidget);
|
||||||
expect(find.descendant(of: pickerFinder, matching: find.text('f/45 - 16"')), findsOneWidget);
|
expect(find.descendant(of: pickerFinder, matching: find.text('f/45 - 1"')), findsOneWidget);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.6 MiB |
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue