diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 14f3950..2079426 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -150,5 +150,9 @@ } } }, - "close": "Close" + "close": "Close", + "filmFormula": "Formula", + "filmFormulaExponential": "T=t^Rf", + "filmFormulaExponentialRf": "Rf", + "name": "Name" } \ No newline at end of file diff --git a/lib/res/theme.dart b/lib/res/theme.dart index 2ae0e13..9f9d392 100644 --- a/lib/res/theme.dart +++ b/lib/res/theme.dart @@ -67,7 +67,8 @@ ThemeData themeFrom(Color primaryColor, Brightness brightness) { style: ListTileStyle.list, iconColor: scheme.onSurface, textColor: scheme.onSurface, - subtitleTextStyle: theme.textTheme.bodyMedium!.copyWith(color: scheme.onSurfaceVariant), + subtitleTextStyle: theme.textTheme.bodyMedium, + leadingAndTrailingTextStyle: theme.textTheme.bodyMedium, ), ); } diff --git a/lib/screens/film_edit/bloc_film_edit.dart b/lib/screens/film_edit/bloc_film_edit.dart index 16fd2e5..1b72289 100644 --- a/lib/screens/film_edit/bloc_film_edit.dart +++ b/lib/screens/film_edit/bloc_film_edit.dart @@ -4,11 +4,18 @@ import 'package:lightmeter/screens/film_edit/state_film_edit.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class FilmEditBloc extends Bloc { + final FilmExponential _originalFilm; + FilmExponential _newFilm; + FilmEditBloc(FilmExponential film) - : super( + : _originalFilm = film, + _newFilm = film, + super( FilmEditState( - film, - IsoValue.values.firstWhere((element) => element.value == film.iso), + name: film.name, + isoValue: IsoValue.values.firstWhere((element) => element.value == film.iso), + exponent: film.exponent, + canSave: false, ), ) { on( @@ -27,18 +34,47 @@ class FilmEditBloc extends Bloc { ); } - Future _onNameChanged(FilmEditNameChangedEvent event, Emitter emit) async {} - - Future _onIsoChanged(FilmEditIsoChangedEvent event, Emitter emit) async { + Future _onNameChanged(FilmEditNameChangedEvent event, Emitter emit) async { + _newFilm = _newFilm.copyWith(name: event.name); emit( FilmEditState( - state.film.copyWith(iso: event.iso.value), - event.iso, + name: event.name, + isoValue: state.isoValue, + exponent: state.exponent, + canSave: _canSave(event.name, state.exponent), ), ); } - Future _onExpChanged(FilmEditExpChangedEvent event, Emitter emit) async {} + Future _onIsoChanged(FilmEditIsoChangedEvent event, Emitter emit) async { + _newFilm = _newFilm.copyWith(iso: event.iso.value); + emit( + FilmEditState( + name: state.name, + isoValue: event.iso, + exponent: state.exponent, + canSave: _canSave(state.name, state.exponent), + ), + ); + } + + Future _onExpChanged(FilmEditExpChangedEvent event, Emitter emit) async { + if (event.exponent != null) { + _newFilm = _newFilm.copyWith(exponent: event.exponent); + } + emit( + FilmEditState( + name: state.name, + isoValue: state.isoValue, + exponent: event.exponent, + canSave: _canSave(state.name, event.exponent), + ), + ); + } Future _onSave(FilmEditSaveEvent _, Emitter emit) async {} + + bool _canSave(String name, double? exponent) { + return name.isNotEmpty && exponent != null && _newFilm != _originalFilm; + } } diff --git a/lib/screens/film_edit/components/exponential_formula_input/widget_input_exponential_formula.dart b/lib/screens/film_edit/components/exponential_formula_input/widget_input_exponential_formula.dart new file mode 100644 index 0000000..3b16739 --- /dev/null +++ b/lib/screens/film_edit/components/exponential_formula_input/widget_input_exponential_formula.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; +import 'package:lightmeter/screens/shared/text_field/widget_text_field.dart'; + +class ExponentialFormulaInput extends StatefulWidget { + final double? value; + final ValueChanged onChanged; + + const ExponentialFormulaInput({ + super.key, + required this.value, + required this.onChanged, + }); + + @override + State createState() => _ExponentialFormulaInputState(); +} + +class _ExponentialFormulaInputState extends State { + TextStyle get style => + Theme.of(context).textTheme.bodyMedium!.copyWith(color: Theme.of(context).listTileTheme.textColor); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + ListTile( + leading: const Icon(Icons.show_chart), + title: Text(S.of(context).filmFormula), + trailing: Text(S.of(context).filmFormulaExponential), + ), + ListTile( + leading: const SizedBox(), + title: Text( + S.of(context).filmFormulaExponentialRf, + style: Theme.of(context).listTileTheme.titleTextStyle, + ), + trailing: SizedBox( + width: _textInputWidth(context), + child: LightmeterTextField( + initialValue: widget.value?.toString() ?? '', + inputFormatters: [FilteringTextInputFormatter.allow(RegExp("[0-9.]"))], + onChanged: (value) { + widget.onChanged(double.tryParse(value)); + }, + textAlign: TextAlign.end, + style: style, + ), + ), + ), + ], + ); + } + + double _textInputWidth(BuildContext context) { + final textPainter = TextPainter( + text: TextSpan(text: widget.value.toString(), style: style), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(); + return textPainter.maxIntrinsicWidth + Dimens.grid4; + } +} diff --git a/lib/screens/film_edit/event_film_edit.dart b/lib/screens/film_edit/event_film_edit.dart index c469451..1b8980d 100644 --- a/lib/screens/film_edit/event_film_edit.dart +++ b/lib/screens/film_edit/event_film_edit.dart @@ -17,9 +17,9 @@ class FilmEditIsoChangedEvent extends FilmEditEvent { } class FilmEditExpChangedEvent extends FilmEditEvent { - final double exp; + final double? exponent; - const FilmEditExpChangedEvent(this.exp); + const FilmEditExpChangedEvent(this.exponent); } class FilmEditSaveEvent extends FilmEditEvent { diff --git a/lib/screens/film_edit/screen_film_edit.dart b/lib/screens/film_edit/screen_film_edit.dart index 9ce089f..6c881d9 100644 --- a/lib/screens/film_edit/screen_film_edit.dart +++ b/lib/screens/film_edit/screen_film_edit.dart @@ -1,13 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/film_edit/bloc_film_edit.dart'; +import 'package:lightmeter/screens/film_edit/components/exponential_formula_input/widget_input_exponential_formula.dart'; import 'package:lightmeter/screens/film_edit/event_film_edit.dart'; import 'package:lightmeter/screens/film_edit/state_film_edit.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/settings/components/shared/dialog_filter/widget_dialog_filter.dart'; -import 'package:lightmeter/screens/settings/components/shared/expandable_section_list/widget_expandable_section_list.dart'; import 'package:lightmeter/screens/shared/sliver_screen/screen_sliver.dart'; +import 'package:lightmeter/screens/shared/text_field/widget_text_field.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class FilmEditScreen extends StatefulWidget { @@ -22,37 +24,54 @@ class _FilmEditScreenState extends State { Widget build(BuildContext context) { return SliverScreen( title: BlocBuilder( - buildWhen: (previous, current) => previous.film.name != current.film.name, - builder: (context, state) => TextFormField( - initialValue: state.film.name, - onChanged: (value) {}, - ), + buildWhen: (previous, current) => previous.name != current.name, + builder: (_, state) => Text(state.name), ), appBarActions: [ - IconButton( - onPressed: () {}, - icon: const Icon(Icons.save), + BlocBuilder( + buildWhen: (previous, current) => previous.canSave != current.canSave, + builder: (context, state) => IconButton( + onPressed: state.canSave ? () {} : null, + icon: const Icon(Icons.save), + ), ), ], slivers: [ SliverToBoxAdapter( - child: BlocBuilder( - buildWhen: (previous, current) => previous.isoValue != current.isoValue, - builder: (context, state) => _IsoPickerListTile( - selected: state.isoValue, - onChanged: (value) { - context.read().add(FilmEditIsoChangedEvent(value)); - }, + child: Padding( + padding: const EdgeInsets.fromLTRB( + Dimens.paddingM, + 0, + Dimens.paddingM, + Dimens.paddingM, ), - ), - ), - SliverToBoxAdapter( - child: BlocBuilder( - buildWhen: (previous, current) => previous.film.exponent != current.film.exponent, - builder: (context, state) => ListTile( - leading: const Icon(Icons.equalizer), - title: Text('Formula'), - trailing: Text(state.film.exponent.toString()), + child: Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM), + child: Column( + children: [ + _NameFieldBuilder(), + BlocBuilder( + buildWhen: (previous, current) => previous.isoValue != current.isoValue, + builder: (context, state) => _IsoPickerListTile( + selected: state.isoValue, + onChanged: (value) { + context.read().add(FilmEditIsoChangedEvent(value)); + }, + ), + ), + BlocBuilder( + buildWhen: (previous, current) => previous.exponent != current.exponent, + builder: (context, state) => ExponentialFormulaInput( + value: state.exponent, + onChanged: (value) { + context.read().add(FilmEditExpChangedEvent(value)); + }, + ), + ), + ], + ), + ), ), ), ), @@ -62,6 +81,33 @@ class _FilmEditScreenState extends State { } } +class _NameFieldBuilder extends StatelessWidget { + const _NameFieldBuilder(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + left: Dimens.paddingM, + top: Dimens.paddingS / 2, + right: Dimens.paddingL, + bottom: Dimens.paddingS / 2, + ), + child: BlocBuilder( + buildWhen: (previous, current) => previous.name != current.name, + builder: (context, state) => LightmeterTextField( + initialValue: state.name, + onChanged: (value) { + context.read().add(FilmEditNameChangedEvent(value)); + }, + style: Theme.of(context).listTileTheme.titleTextStyle, + leading: const Icon(Icons.edit_outlined), + ), + ), + ); + } +} + class _IsoPickerListTile extends StatelessWidget { final IsoValue selected; final ValueChanged onChanged; diff --git a/lib/screens/film_edit/state_film_edit.dart b/lib/screens/film_edit/state_film_edit.dart index 0fac707..b811160 100644 --- a/lib/screens/film_edit/state_film_edit.dart +++ b/lib/screens/film_edit/state_film_edit.dart @@ -1,8 +1,15 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class FilmEditState { - final FilmExponential film; + final String name; final IsoValue isoValue; + final double? exponent; + final bool canSave; - const FilmEditState(this.film, this.isoValue); + const FilmEditState({ + required this.name, + required this.isoValue, + required this.exponent, + required this.canSave, + }); } diff --git a/lib/screens/shared/text_field/widget_text_field.dart b/lib/screens/shared/text_field/widget_text_field.dart new file mode 100644 index 0000000..d9523d8 --- /dev/null +++ b/lib/screens/shared/text_field/widget_text_field.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class LightmeterTextField extends TextFormField { + LightmeterTextField({ + super.controller, + super.focusNode, + super.initialValue, + super.inputFormatters, + super.onChanged, + super.style, + super.textAlign, + Widget? leading, + }) : super( + autovalidateMode: AutovalidateMode.onUserInteraction, + maxLines: 1, + decoration: InputDecoration( + contentPadding: EdgeInsets.zero, + errorStyle: const TextStyle(fontSize: 0), + icon: leading, + ), + validator: (value) { + if (value == null || value.isEmpty) { + return ''; + } else { + return null; + } + }, + ); +}