import 'package:flutter/material.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:fladder/providers/settings/subtitle_settings_provider.dart'; import 'package:fladder/screens/shared/flat_button.dart'; import 'package:fladder/util/list_padding.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/widget_extensions.dart'; import 'package:fladder/widgets/shared/fladder_slider.dart'; Future showSubtitleControls({ required BuildContext context, String? label, }) async { await showDialog( context: context, barrierColor: Colors.black.withValues(alpha: 0.1), builder: (context) => AlertDialog( backgroundColor: Colors.transparent, elevation: 0, content: ConstrainedBox( constraints: BoxConstraints(minWidth: MediaQuery.sizeOf(context).width * 0.75), child: VideoSubtitleControls(label: label)), ), ); return; } class VideoSubtitleControls extends ConsumerStatefulWidget { final String? label; const VideoSubtitleControls({this.label, super.key}); @override ConsumerState createState() => _VideoSubtitleControlsState(); } class _VideoSubtitleControlsState extends ConsumerState { late final lastSettings = ref.read(subtitleSettingsProvider); final controller = ScrollController(); Key? activeKey; bool showPartial = true; bool hideControls = false; void setOpacity(Key? key) => setState(() { activeKey = key; showPartial = !(activeKey != null); }); @override Widget build(BuildContext context) { final subSettings = ref.watch(subtitleSettingsProvider); final provider = ref.read(subtitleSettingsProvider.notifier); final controlsHidden = hideControls ? false : showPartial; return AnimatedContainer( height: MediaQuery.sizeOf(context).width * 0.85, width: MediaQuery.sizeOf(context).height * 0.7, duration: const Duration(milliseconds: 250), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: controlsHidden ? Theme.of(context).colorScheme.surfaceContainer.withValues(alpha: 0.8) : Colors.transparent, ), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ if (widget.label?.isNotEmpty == true) Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( widget.label!, style: Theme.of(context).textTheme.headlineMedium, ), IconButton( onPressed: () => context.maybePop(), icon: const Icon(IconsaxPlusBold.close_circle), ) ], ).addVisiblity(activeKey == null ? controlsHidden : activeKey == const Key('title')), IconButton.filledTonal( isSelected: !hideControls, onPressed: () => setState(() => hideControls = !hideControls), icon: Icon(hideControls ? Icons.visibility_rounded : Icons.visibility_off_rounded), ), Flexible( child: IgnorePointer( ignoring: !controlsHidden, child: Scrollbar( thumbVisibility: controlsHidden, controller: controller, child: SingleChildScrollView( controller: controller, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: subSettings != lastSettings ? () => provider.resetSettings(value: lastSettings) : null, child: Text(context.localized.clearChanges), ), const SizedBox(width: 32), ElevatedButton( onPressed: () => provider.resetSettings(), child: Text(context.localized.useDefaults), ), ], ).addVisiblity(controlsHidden), SegmentedButton( showSelectedIcon: false, multiSelectionEnabled: false, segments: [ ButtonSegment( label: Text(context.localized.light, style: const TextStyle(fontWeight: FontWeight.w100)), value: FontWeight.w100, ), ButtonSegment( label: Text(context.localized.normal, style: const TextStyle(fontWeight: FontWeight.w500)), value: FontWeight.normal, ), ButtonSegment( label: Text(context.localized.bold, style: const TextStyle(fontWeight: FontWeight.w900)), value: FontWeight.bold, ), ], selected: {subSettings.fontWeight}, onSelectionChanged: (p0) { provider.setFontWeight(p0.first); }, ).addVisiblity(controlsHidden), Column( children: [ Row( children: [ const Icon(Icons.format_size_rounded), Flexible( child: FladderSlider( min: 8.0, max: 160.0, onChangeStart: (value) => setOpacity(const Key('fontSize')), onChangeEnd: (value) => setOpacity(null), value: subSettings.fontSize.clamp(8.0, 160.0), onChanged: (value) => provider.setFontSize(value.ceilToDouble()), ), ), ConstrainedBox( constraints: const BoxConstraints(minWidth: 35), child: Text( subSettings.fontSize.toStringAsFixed(0), textAlign: TextAlign.center, ), ), ], ), Text(context.localized.fontSize), ], ).addVisiblity(activeKey == null ? controlsHidden : activeKey == const Key('fontSize')), Column( children: [ Row( children: [ const Icon(Icons.height_rounded), Flexible( child: FladderSlider( min: 0.0, max: 1.0, divisions: 80, onChangeStart: (value) => setOpacity(const Key('verticalOffset')), onChangeEnd: (value) => setOpacity(null), value: subSettings.verticalOffset.clamp(0, 1), onChanged: (value) => provider.setVerticalOffset(value), ), ), ConstrainedBox( constraints: const BoxConstraints( minWidth: 35, ), child: Text( subSettings.verticalOffset.toStringAsFixed(2), textAlign: TextAlign.center, ), ), ], ), Text(context.localized.heightOffset), ], ).addVisiblity(activeKey == null ? controlsHidden : activeKey == const Key('verticalOffset')), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Icon(Icons.color_lens_rounded), ...[Colors.white, Colors.yellow, Colors.black, Colors.grey].map( (e) => FlatButton( onTap: () => provider.setSubColor(e), borderRadiusGeometry: BorderRadius.circular(5), clipBehavior: Clip.hardEdge, child: Container( height: 25, width: 25, color: e, ), ), ), ], ), Text(context.localized.fontColor), ], ).addVisiblity(controlsHidden), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Icon(Icons.border_color_rounded), ...[Colors.white, Colors.yellow, Colors.black, Colors.grey, Colors.transparent].map( (e) => FlatButton( onTap: () => provider .setOutlineColor(e == Colors.transparent ? e : e.withValues(alpha: 0.85)), borderRadiusGeometry: BorderRadius.circular(5), clipBehavior: Clip.hardEdge, child: Container( height: 25, width: 25, color: e == Colors.transparent ? Colors.white : e, child: e == Colors.transparent ? const Icon( Icons.disabled_by_default_outlined, color: Colors.red, ) : null, ), ), ), ], ), Text(context.localized.outlineColor), ], ).addVisiblity(controlsHidden), Column( children: [ Row( children: [ const Icon(Icons.border_style), Flexible( child: FladderSlider( min: 1, max: 25, divisions: 24, onChangeStart: (value) => setOpacity(const Key('outlineSize')), onChangeEnd: (value) => setOpacity(null), value: subSettings.outlineSize.clamp(1, 24), onChanged: (value) => provider.setOutlineThickness(value), ), ), ConstrainedBox( constraints: const BoxConstraints( minWidth: 35, ), child: Text( subSettings.outlineSize.toStringAsFixed(2), textAlign: TextAlign.center, ), ), ], ), Text(context.localized.outlineSize), ], ).addVisiblity(activeKey == null ? controlsHidden : activeKey == const Key('outlineSize')), Column( children: [ Row( children: [ const Icon(Icons.square_rounded), Flexible( child: FladderSlider( min: 0, max: 1, divisions: 20, onChangeStart: (value) => setOpacity(const Key('backGroundOpacity')), onChangeEnd: (value) => setOpacity(null), value: subSettings.backGroundColor.a.clamp(0, 1), onChanged: (value) => provider.setBackGroundOpacity(value), ), ), ConstrainedBox( constraints: const BoxConstraints( minWidth: 35, ), child: Text( subSettings.backGroundColor.a.toStringAsFixed(2), textAlign: TextAlign.center, ), ), ], ), Text(context.localized.backgroundOpacity), ], ).addVisiblity( activeKey == null ? controlsHidden : activeKey == const Key('backGroundOpacity')), Column( children: [ Row( children: [ const Icon(Icons.blur_circular_rounded), Flexible( child: FladderSlider( min: 0, max: 1, divisions: 20, value: subSettings.shadow.clamp(0, 1), onChangeStart: (value) => setOpacity(const Key('shadowSlider')), onChangeEnd: (value) => setOpacity(null), onChanged: (value) => provider.setShadowIntensity(value), ), ), ConstrainedBox( constraints: const BoxConstraints( minWidth: 35, ), child: Text( subSettings.shadow.toStringAsFixed(2), textAlign: TextAlign.center, ), ), ], ), Text(context.localized.shadow) ], ).addVisiblity(activeKey == null ? controlsHidden : activeKey == const Key('shadowSlider')), ].addPadding(const EdgeInsets.symmetric(vertical: 12)), ), ), ), ), ), ), ], ), ), ); } }