discard aperture when unselecting a pinhole profile

This commit is contained in:
Vadim 2025-09-08 17:05:17 +02:00
parent 232a9316cd
commit c40354c62b
7 changed files with 67 additions and 39 deletions

View file

@ -12,8 +12,15 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
LogbookPhotoEditBloc( LogbookPhotoEditBloc(
this.photosProvider, this.photosProvider,
LogbookPhoto photo, LogbookPhoto photo,
IEquipmentProfile? equipmentProfile,
) : _originalPhoto = photo, ) : _originalPhoto = photo,
_newPhoto = photo, _newPhoto = photo,
assert(
equipmentProfile == null ||
equipmentProfile is! PinholeEquipmentProfile ||
photo.apertureValue != null && equipmentProfile.aperture == photo.apertureValue!.rawValue,
"Aperture value must be the same as the equipment profile's aperture value if the equipment profile is a pinhole profile",
),
super( super(
LogbookPhotoEditState( LogbookPhotoEditState(
id: photo.id, id: photo.id,
@ -25,7 +32,7 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
coordinates: photo.coordinates, coordinates: photo.coordinates,
aperture: photo.apertureValue, aperture: photo.apertureValue,
shutterSpeed: photo.shutterSpeedValue, shutterSpeed: photo.shutterSpeedValue,
equipmentProfileId: photo.equipmentProfileId, equipmentProfile: equipmentProfile,
filmId: photo.filmId, filmId: photo.filmId,
note: photo.note, note: photo.note,
canSave: false, canSave: false,
@ -74,10 +81,22 @@ class LogbookPhotoEditBloc extends Bloc<LogbookPhotoEditEvent, LogbookPhotoEditS
} }
Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async { Future<void> _onEquipmentProfileChanged(LogbookPhotoEquipmentProfileChangedEvent event, Emitter emit) async {
_newPhoto = _newPhoto.copyWith(equipmentProfileId: Optional(event.equipmentProfileId)); final equipmentProfile = event.equipmentProfile;
Optional<ApertureValue>? apertureValue;
if (state.equipmentProfile is PinholeEquipmentProfile &&
(equipmentProfile == null || equipmentProfile is EquipmentProfile)) {
apertureValue = const Optional(null);
} else if (equipmentProfile is PinholeEquipmentProfile) {
apertureValue = Optional(ApertureValue(equipmentProfile.aperture, StopType.full));
}
_newPhoto = _newPhoto.copyWith(
apertureValue: apertureValue,
equipmentProfileId: Optional(equipmentProfile?.id),
);
emit( emit(
state.copyWith( state.copyWith(
equipmentProfileId: Optional(event.equipmentProfileId), aperture: apertureValue,
equipmentProfile: Optional(event.equipmentProfile),
canSave: _canSave(), canSave: _canSave(),
), ),
); );

View file

@ -9,7 +9,7 @@ class PickerListTile<T> extends StatelessWidget {
final T? selectedValue; final T? selectedValue;
final List<T> values; final List<T> values;
final String Function(T) titleAdapter; final String Function(T) titleAdapter;
final ValueChanged<Optional<T>> onChanged; final ValueChanged<Optional<T>>? onChanged;
const PickerListTile({ const PickerListTile({
required this.icon, required this.icon,
@ -27,7 +27,8 @@ class PickerListTile<T> extends StatelessWidget {
leading: Icon(icon), leading: Icon(icon),
title: Text(title), title: Text(title),
trailing: Text(_titleAdapter(context, selectedValue)), trailing: Text(_titleAdapter(context, selectedValue)),
onTap: () { onTap: onChanged != null
? () {
showDialog<Optional<T>>( showDialog<Optional<T>>(
context: context, context: context,
builder: (_) => DialogPicker<Optional<T>>( builder: (_) => DialogPicker<Optional<T>>(
@ -44,10 +45,11 @@ class PickerListTile<T> extends StatelessWidget {
), ),
).then((value) { ).then((value) {
if (value != null) { if (value != null) {
onChanged(value); onChanged!(value);
} }
}); });
}, }
: null,
); );
} }

View file

@ -23,9 +23,9 @@ class LogbookPhotoNoteChangedEvent extends LogbookPhotoEditEvent {
} }
class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoEquipmentProfileChangedEvent extends LogbookPhotoEditEvent {
final String? equipmentProfileId; final IEquipmentProfile? equipmentProfile;
const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfileId); const LogbookPhotoEquipmentProfileChangedEvent(this.equipmentProfile);
} }
class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent { class LogbookPhotoFilmChangedEvent extends LogbookPhotoEditEvent {

View file

@ -1,5 +1,7 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/providers/equipment_profile_provider.dart';
import 'package:lightmeter/providers/logbook_photos_provider.dart'; import 'package:lightmeter/providers/logbook_photos_provider.dart';
import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart'; import 'package:lightmeter/screens/logbook_photo_edit/bloc_logbook_photo_edit.dart';
import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart'; import 'package:lightmeter/screens/logbook_photo_edit/screen_logbook_photo_edit.dart';
@ -25,6 +27,7 @@ class LogbookPhotoEditFlow extends StatelessWidget {
create: (_) => LogbookPhotoEditBloc( create: (_) => LogbookPhotoEditBloc(
LogbookPhotosProvider.of(context), LogbookPhotosProvider.of(context),
args.photo, args.photo,
EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == args.photo.equipmentProfileId),
), ),
child: const LogbookPhotoEditScreen(), child: const LogbookPhotoEditScreen(),
); );

View file

@ -224,13 +224,15 @@ class _AperturePickerListTile extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>( return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.aperture != current.aperture, buildWhen: (previous, current) => previous.aperture != current.aperture,
builder: (context, state) => PickerListTile( builder: (context, state) => PickerListTile<ApertureValue>(
icon: Icons.camera_outlined, icon: Icons.camera_outlined,
title: S.of(context).apertureValue, title: S.of(context).apertureValue,
values: ApertureValue.values, values: ApertureValue.values,
selectedValue: state.aperture, selectedValue: state.aperture,
titleAdapter: (value) => value.toString(), titleAdapter: (value) => value.toString(),
onChanged: (value) { onChanged: state.equipmentProfile is PinholeEquipmentProfile
? null
: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoApertureChangedEvent(value.value)); context.read<LogbookPhotoEditBloc>().add(LogbookPhotoApertureChangedEvent(value.value));
}, },
), ),
@ -265,15 +267,15 @@ class _EquipmentProfilePickerListTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>( return BlocBuilder<LogbookPhotoEditBloc, LogbookPhotoEditState>(
buildWhen: (previous, current) => previous.equipmentProfileId != current.equipmentProfileId, buildWhen: (previous, current) => previous.equipmentProfile != current.equipmentProfile,
builder: (context, state) => PickerListTile( builder: (context, state) => PickerListTile(
icon: Icons.camera_alt_outlined, icon: Icons.camera_alt_outlined,
title: S.of(context).equipmentProfile, title: S.of(context).equipmentProfile,
values: EquipmentProfiles.of(context).skip(1).toList(growable: false), values: EquipmentProfiles.of(context).skip(1).toList(growable: false),
selectedValue: EquipmentProfiles.of(context).firstWhereOrNull((e) => e.id == state.equipmentProfileId), selectedValue: state.equipmentProfile,
titleAdapter: (value) => value.name, titleAdapter: (value) => value.name,
onChanged: (value) { onChanged: (value) {
context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value?.id)); context.read<LogbookPhotoEditBloc>().add(LogbookPhotoEquipmentProfileChangedEvent(value.value));
}, },
), ),
); );

View file

@ -10,7 +10,7 @@ class LogbookPhotoEditState {
final Coordinates? coordinates; final Coordinates? coordinates;
final ApertureValue? aperture; final ApertureValue? aperture;
final ShutterSpeedValue? shutterSpeed; final ShutterSpeedValue? shutterSpeed;
final String? equipmentProfileId; final IEquipmentProfile? equipmentProfile;
final String? filmId; final String? filmId;
final String? note; final String? note;
final bool canSave; final bool canSave;
@ -26,7 +26,7 @@ class LogbookPhotoEditState {
this.coordinates, this.coordinates,
this.aperture, this.aperture,
this.shutterSpeed, this.shutterSpeed,
this.equipmentProfileId, this.equipmentProfile,
this.filmId, this.filmId,
this.note, this.note,
required this.canSave, required this.canSave,
@ -37,7 +37,7 @@ class LogbookPhotoEditState {
String? name, String? name,
Optional<ApertureValue>? aperture, Optional<ApertureValue>? aperture,
Optional<ShutterSpeedValue>? shutterSpeed, Optional<ShutterSpeedValue>? shutterSpeed,
Optional<String>? equipmentProfileId, Optional<IEquipmentProfile>? equipmentProfile,
Optional<String>? filmId, Optional<String>? filmId,
String? note, String? note,
bool? canSave, bool? canSave,
@ -52,7 +52,7 @@ class LogbookPhotoEditState {
nd: nd, nd: nd,
aperture: aperture != null ? aperture.value : this.aperture, aperture: aperture != null ? aperture.value : this.aperture,
shutterSpeed: shutterSpeed != null ? shutterSpeed.value : this.shutterSpeed, shutterSpeed: shutterSpeed != null ? shutterSpeed.value : this.shutterSpeed,
equipmentProfileId: equipmentProfileId != null ? equipmentProfileId.value : this.equipmentProfileId, equipmentProfile: equipmentProfile != null ? equipmentProfile.value : this.equipmentProfile,
filmId: filmId != null ? filmId.value : this.filmId, filmId: filmId != null ? filmId.value : this.filmId,
note: note ?? this.note, note: note ?? this.note,
canSave: canSave ?? this.canSave, canSave: canSave ?? this.canSave,

View file

@ -26,6 +26,7 @@ class DialogPicker<T> extends StatefulWidget {
class _DialogPickerState<T> extends State<DialogPicker<T>> { class _DialogPickerState<T> extends State<DialogPicker<T>> {
late T _selected = widget.selectedValue; late T _selected = widget.selectedValue;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
bool _hasSelection = false;
@override @override
void initState() { void initState() {
@ -34,6 +35,7 @@ class _DialogPickerState<T> extends State<DialogPicker<T>> {
final selectedIndex = widget.values.indexOf(_selected); final selectedIndex = widget.values.indexOf(_selected);
if (selectedIndex >= 0) { if (selectedIndex >= 0) {
_scrollController.jumpTo((Dimens.grid56 * selectedIndex).clamp(0, _scrollController.position.maxScrollExtent)); _scrollController.jumpTo((Dimens.grid56 * selectedIndex).clamp(0, _scrollController.position.maxScrollExtent));
_hasSelection = true;
} }
}); });
} }
@ -93,7 +95,7 @@ class _DialogPickerState<T> extends State<DialogPicker<T>> {
child: Text(S.of(context).cancel), child: Text(S.of(context).cancel),
), ),
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(_selected), onPressed: _hasSelection ? () => Navigator.of(context).pop(_selected) : null,
child: Text(S.of(context).select), child: Text(S.of(context).select),
), ),
], ],