diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 57e453a58..caca32fc1 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -5,6 +5,7 @@ import 'package:PiliPlus/pages/danmaku/controller.dart'; import 'package:PiliPlus/pages/danmaku/danmaku_model.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; +import 'package:PiliPlus/plugin/pl_player/utils/danmaku_options.dart'; import 'package:PiliPlus/utils/danmaku_utils.dart'; import 'package:canvas_danmaku/canvas_danmaku.dart'; import 'package:flutter/material.dart'; @@ -29,6 +30,8 @@ class PlDanmaku extends StatefulWidget { @override State createState() => _PlDanmakuState(); + + bool get notFullscreen => !isFullScreen || isPipMode; } class _PlDanmakuState extends State { @@ -65,22 +68,13 @@ class _PlDanmakuState extends State { @override void didUpdateWidget(PlDanmaku oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.isPipMode != widget.isPipMode || - oldWidget.isFullScreen != widget.isFullScreen) { - _updateFontSize(); + if (oldWidget.notFullscreen != widget.notFullscreen) { + _controller?.updateOption( + DanmakuOptions.get(notFullscreen: widget.notFullscreen), + ); } } - void _updateFontSize() { - _controller?.updateOption( - _controller!.option.copyWith(fontSize: _fontSize), - ); - } - - double get _fontSize => !widget.isFullScreen || widget.isPipMode - ? 15 * playerController.danmakuFontScale - : 15 * playerController.danmakuFontScaleFS; - // 播放器状态监听 void playerListener(PlayerStatus? status) { if (status == PlayerStatus.playing) { @@ -114,7 +108,7 @@ class _PlDanmakuState extends State { List? currentDanmakuList = _plDanmakuController .getCurrentDanmaku(currentPosition); if (currentDanmakuList != null) { - final blockColorful = playerController.blockColorful; + final blockColorful = DanmakuOptions.blockColorful; for (DanmakuElem e in currentDanmakuList) { if (e.mode == 7) { try { @@ -177,22 +171,9 @@ class _PlDanmakuState extends State { createdController: (e) { playerController.danmakuController = _controller = e; }, - option: DanmakuOption( - fontSize: _fontSize, - fontWeight: playerController.danmakuFontWeight, - area: playerController.showArea, - hideTop: playerController.blockTypes.contains(5), - hideScroll: playerController.blockTypes.contains(2), - hideBottom: playerController.blockTypes.contains(4), - hideSpecial: playerController.blockTypes.contains(7), - duration: - playerController.danmakuDuration / - playerController.playbackSpeed, - staticDuration: - playerController.danmakuStaticDuration / - playerController.playbackSpeed, - strokeWidth: playerController.danmakuStrokeWidth, - lineHeight: playerController.danmakuLineHeight, + option: DanmakuOptions.get( + notFullscreen: widget.notFullscreen, + speed: playerController.playbackSpeed, ), ), ), diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 8bd8f9ca8..9266c3b55 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -19,6 +19,7 @@ import 'package:PiliPlus/pages/live_room/send_danmaku/view.dart'; import 'package:PiliPlus/pages/video/widgets/header_control.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/data_source.dart'; +import 'package:PiliPlus/plugin/pl_player/utils/danmaku_options.dart'; import 'package:PiliPlus/services/service_locator.dart'; import 'package:PiliPlus/tcp/live.dart'; import 'package:PiliPlus/utils/accounts.dart'; @@ -433,7 +434,7 @@ class LiveRoomController extends GetxController { danmakuController?.addDanmaku( DanmakuContentItem( msg, - color: plPlayerController.blockColorful + color: DanmakuOptions.blockColorful ? Colors.white : DmUtils.decimalToColor(extra['color']), type: DmUtils.getPosition(extra['mode']), diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 9421c69e7..c3674d7f1 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -20,6 +20,7 @@ import 'package:PiliPlus/pages/live_room/widgets/header_control.dart'; import 'package:PiliPlus/pages/video/widgets/player_focus.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_status.dart'; +import 'package:PiliPlus/plugin/pl_player/utils/danmaku_options.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; import 'package:PiliPlus/plugin/pl_player/view.dart'; import 'package:PiliPlus/services/service_locator.dart'; @@ -974,6 +975,8 @@ class LiveDanmaku extends StatefulWidget { @override State createState() => _LiveDanmakuState(); + + bool get notFullscreen => !isFullScreen || isPipMode; } class _LiveDanmakuState extends State { @@ -982,27 +985,13 @@ class _LiveDanmakuState extends State { @override void didUpdateWidget(LiveDanmaku oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.isPipMode != widget.isPipMode || - oldWidget.isFullScreen != widget.isFullScreen) { - _updateFontSize(); + if (oldWidget.notFullscreen != widget.notFullscreen) { + plPlayerController.danmakuController?.updateOption( + DanmakuOptions.get(notFullscreen: widget.notFullscreen), + ); } } - void _updateFontSize() { - plPlayerController.danmakuController?.updateOption( - plPlayerController.danmakuController!.option.copyWith( - fontSize: _fontSize, - ), - ); - } - - double get _fontSize => - plPlayerController.isDesktopPip || - !widget.isFullScreen || - widget.isPipMode - ? 15 * plPlayerController.danmakuFontScale - : 15 * plPlayerController.danmakuFontScaleFS; - @override Widget build(BuildContext context) { return Obx( @@ -1017,22 +1006,7 @@ class _LiveDanmakuState extends State { widget.liveRoomController.danmakuController = plPlayerController.danmakuController = e; }, - option: DanmakuOption( - fontSize: _fontSize, - fontWeight: plPlayerController.danmakuFontWeight, - area: plPlayerController.showArea, - hideTop: plPlayerController.blockTypes.contains(5), - hideScroll: plPlayerController.blockTypes.contains(2), - hideBottom: plPlayerController.blockTypes.contains(4), - duration: - plPlayerController.danmakuDuration / - plPlayerController.playbackSpeed, - staticDuration: - plPlayerController.danmakuStaticDuration / - plPlayerController.playbackSpeed, - strokeWidth: plPlayerController.danmakuStrokeWidth, - lineHeight: plPlayerController.danmakuLineHeight, - ), + option: DanmakuOptions.get(notFullscreen: widget.notFullscreen), ), ); }, diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index f510270c0..345792da7 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -1,7 +1,6 @@ import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/pages/live_room/controller.dart'; -import 'package:PiliPlus/pages/video/widgets/header_control.dart' - show HeaderMixin; +import 'package:PiliPlus/pages/video/widgets/header_mixin.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/video_fit_type.dart'; import 'package:PiliPlus/plugin/pl_player/widgets/common_btn.dart'; diff --git a/lib/pages/video/widgets/header_control.dart b/lib/pages/video/widgets/header_control.dart index c77e80194..09040a77b 100644 --- a/lib/pages/video/widgets/header_control.dart +++ b/lib/pages/video/widgets/header_control.dart @@ -25,13 +25,13 @@ import 'package:PiliPlus/models_new/video/video_play_info/subtitle.dart'; import 'package:PiliPlus/pages/common/common_intro_controller.dart'; import 'package:PiliPlus/pages/danmaku/danmaku_model.dart'; import 'package:PiliPlus/pages/setting/widgets/select_dialog.dart'; -import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; import 'package:PiliPlus/pages/video/controller.dart'; import 'package:PiliPlus/pages/video/introduction/local/controller.dart'; import 'package:PiliPlus/pages/video/introduction/pgc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/controller.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/action_item.dart'; import 'package:PiliPlus/pages/video/introduction/ugc/widgets/menu_row.dart'; +import 'package:PiliPlus/pages/video/widgets/header_mixin.dart'; import 'package:PiliPlus/plugin/pl_player/controller.dart'; import 'package:PiliPlus/plugin/pl_player/models/play_repeat.dart'; import 'package:PiliPlus/plugin/pl_player/utils/fullscreen.dart'; @@ -169,636 +169,6 @@ mixin TimeBatteryMixin on State { } } -mixin HeaderMixin on State { - PlPlayerController get plPlayerController; - - bool get isFullScreen => plPlayerController.isFullScreen.value; - - void showBottomSheet(StatefulWidgetBuilder builder, {double? padding}) { - PageUtils.showVideoBottomSheet( - context, - isFullScreen: () => isFullScreen, - padding: padding, - child: StatefulBuilder( - builder: (context, setState) => plPlayerController.darkVideoPage - ? Theme( - data: Theme.of(this.context), - child: builder(this.context, setState), - ) - : builder(context, setState), - ), - ); - } - - Widget resetBtn( - ThemeData theme, - Object def, - VoidCallback onPressed, { - bool isDanmaku = true, - }) { - return iconButton( - tooltip: '默认值: $def', - icon: const Icon(Icons.refresh), - onPressed: () { - onPressed(); - if (isDanmaku) { - plPlayerController.putDanmakuSettings(); - } else { - plPlayerController.putSubtitleSettings(); - } - }, - iconColor: theme.colorScheme.outline, - size: 24, - iconSize: 24, - ); - } - - /// 弹幕功能 - void showSetDanmaku({bool isLive = false}) { - // 屏蔽类型 - const blockTypesList = [ - (value: 2, label: '滚动'), - (value: 5, label: '顶部'), - (value: 4, label: '底部'), - (value: 6, label: '彩色'), - (value: 7, label: '高级'), - ]; - final blockTypes = plPlayerController.blockTypes; - // 智能云屏蔽 - int danmakuWeight = plPlayerController.danmakuWeight; - // 显示区域 - double showArea = plPlayerController.showArea; - // 不透明度 - double danmakuOpacity = plPlayerController.danmakuOpacity.value; - // 字体大小 - double danmakuFontScale = plPlayerController.danmakuFontScale; - // 全屏字体大小 - double danmakuFontScaleFS = plPlayerController.danmakuFontScaleFS; - double danmakuLineHeight = plPlayerController.danmakuLineHeight; - // 弹幕速度 - double danmakuDuration = plPlayerController.danmakuDuration; - double danmakuStaticDuration = plPlayerController.danmakuStaticDuration; - // 弹幕描边 - double danmakuStrokeWidth = plPlayerController.danmakuStrokeWidth; - // 字体粗细 - int danmakuFontWeight = plPlayerController.danmakuFontWeight; - bool massiveMode = plPlayerController.massiveMode; - - final DanmakuController? danmakuController = - plPlayerController.danmakuController; - - final isFullScreen = this.isFullScreen; - - showBottomSheet( - (context, setState) { - final theme = Theme.of(context); - - final sliderTheme = SliderThemeData( - trackHeight: 10, - trackShape: const MSliderTrackShape(), - thumbColor: theme.colorScheme.primary, - activeTrackColor: theme.colorScheme.primary, - inactiveTrackColor: theme.colorScheme.onInverseSurface, - thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6.0), - ); - - void updateLineHeight(double val) { - plPlayerController.danmakuLineHeight = danmakuLineHeight = val - .toPrecision(1); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - lineHeight: danmakuLineHeight, - ), - ); - } catch (_) {} - } - - void updateDuration(double val) { - plPlayerController.danmakuDuration = danmakuDuration = val - .toPrecision(1); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - duration: danmakuDuration / plPlayerController.playbackSpeed, - ), - ); - } catch (_) {} - } - - void updateStaticDuration(double val) { - plPlayerController.danmakuStaticDuration = danmakuStaticDuration = val - .toPrecision(1); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - staticDuration: - danmakuStaticDuration / plPlayerController.playbackSpeed, - ), - ); - } catch (_) {} - } - - void updateFontSizeFS(double val) { - plPlayerController.danmakuFontScaleFS = danmakuFontScaleFS = val; - setState(() {}); - if (isFullScreen) { - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - fontSize: (15 * danmakuFontScaleFS).toDouble(), - ), - ); - } catch (_) {} - } - } - - void updateFontSize(double val) { - plPlayerController.danmakuFontScale = danmakuFontScale = val; - setState(() {}); - if (!isFullScreen) { - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - fontSize: (15 * danmakuFontScale).toDouble(), - ), - ); - } catch (_) {} - } - } - - void updateStrokeWidth(double val) { - plPlayerController.danmakuStrokeWidth = danmakuStrokeWidth = val; - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - strokeWidth: danmakuStrokeWidth, - ), - ); - } catch (_) {} - } - - void updateFontWeight(double val) { - plPlayerController.danmakuFontWeight = danmakuFontWeight = val - .toInt(); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith(fontWeight: danmakuFontWeight), - ); - } catch (_) {} - } - - void updateOpacity(double val) { - plPlayerController.danmakuOpacity.value = danmakuOpacity = val; - setState(() {}); - } - - void updateShowArea(double val) { - plPlayerController.showArea = showArea = val.toPrecision(1); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith(area: showArea), - ); - } catch (_) {} - } - - void updateDanmakuWeight(double val) { - plPlayerController.danmakuWeight = danmakuWeight = val.toInt(); - setState(() {}); - } - - void onUpdateBlockType(int blockType, bool blocked) { - if (blocked) { - blockTypes.remove(blockType); - } else { - blockTypes.add(blockType); - } - plPlayerController - ..blockTypes = blockTypes - ..blockColorful = blockTypes.contains(6) - ..putDanmakuSettings(); - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith( - hideTop: blockTypes.contains(5), - hideBottom: blockTypes.contains(4), - hideScroll: blockTypes.contains(2), - hideSpecial: blockTypes.contains(7), - // 添加或修改其他需要修改的选项属性 - ), - ); - } catch (_) {} - } - - return Padding( - padding: const EdgeInsets.all(12), - child: Material( - clipBehavior: Clip.hardEdge, - color: theme.colorScheme.surface, - borderRadius: const BorderRadius.all(Radius.circular(12)), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 14), - child: ListView( - padding: EdgeInsets.zero, - children: [ - const SizedBox( - height: 45, - child: Center( - child: Text('弹幕设置', style: TextStyle(fontSize: 14)), - ), - ), - const SizedBox(height: 10), - if (!isLive) ...[ - Row( - children: [ - Text('智能云屏蔽 $danmakuWeight 级'), - const Spacer(), - TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: Size.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), - onPressed: () => Get - ..back() - ..toNamed( - '/danmakuBlock', - arguments: plPlayerController, - ), - child: Text( - "屏蔽管理(${plPlayerController.filters.count})", - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0, - max: 10, - value: danmakuWeight.toDouble(), - divisions: 10, - label: '$danmakuWeight', - onChanged: updateDanmakuWeight, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - ], - const Text('按类型屏蔽'), - Padding( - padding: const EdgeInsets.only(top: 12), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - spacing: 10, - children: blockTypesList.map( - (e) { - final blocked = blockTypes.contains(e.value); - return ActionRowLineItem( - onTap: () => onUpdateBlockType(e.value, blocked), - text: e.label, - selectStatus: blocked, - ); - }, - ).toList(), - ), - ), - ), - SetSwitchItem( - title: '海量弹幕', - contentPadding: EdgeInsets.zero, - titleStyle: const TextStyle(fontSize: 14), - defaultVal: massiveMode, - setKey: SettingBoxKey.danmakuMassiveMode, - onChanged: (value) { - massiveMode = value; - plPlayerController.massiveMode = value; - setState(() {}); - try { - danmakuController?.updateOption( - danmakuController.option.copyWith(massiveMode: value), - ); - } catch (_) {} - }, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('显示区域 ${showArea * 100}%'), - resetBtn( - theme, - '50.0%', - () => updateShowArea(0.5), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0.1, - max: 1, - value: showArea, - divisions: 9, - label: '${showArea * 100}%', - onChanged: updateShowArea, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('不透明度 ${danmakuOpacity * 100}%'), - resetBtn( - theme, - '100.0%', - () => updateOpacity(1.0), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0, - max: 1, - value: danmakuOpacity, - divisions: 10, - label: '${danmakuOpacity * 100}%', - onChanged: updateOpacity, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('字体粗细 ${danmakuFontWeight + 1}(可能无法精确调节)'), - resetBtn( - theme, - 6, - () => updateFontWeight(5), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0, - max: 8, - value: danmakuFontWeight.toDouble(), - divisions: 8, - label: '${danmakuFontWeight + 1}', - onChanged: updateFontWeight, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('描边粗细 $danmakuStrokeWidth'), - resetBtn( - theme, - 1.5, - () => updateStrokeWidth(1.5), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0, - max: 5, - value: danmakuStrokeWidth, - divisions: 10, - label: '$danmakuStrokeWidth', - onChanged: updateStrokeWidth, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '字体大小 ${(danmakuFontScale * 100).toStringAsFixed(1)}%', - ), - resetBtn( - theme, - '100.0%', - () => updateFontSize(1.0), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0.5, - max: 2.5, - value: danmakuFontScale, - divisions: 20, - label: - '${(danmakuFontScale * 100).toStringAsFixed(1)}%', - onChanged: updateFontSize, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '全屏字体大小 ${(danmakuFontScaleFS * 100).toStringAsFixed(1)}%', - ), - resetBtn( - theme, - '120.0%', - () => updateFontSizeFS(1.2), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 0.5, - max: 2.5, - value: danmakuFontScaleFS, - divisions: 20, - label: - '${(danmakuFontScaleFS * 100).toStringAsFixed(1)}%', - onChanged: updateFontSizeFS, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('滚动弹幕时长 $danmakuDuration 秒'), - resetBtn( - theme, - 7.0, - () => updateDuration(7.0), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 1, - max: 50, - value: danmakuDuration, - divisions: 49, - label: danmakuDuration.toString(), - onChanged: updateDuration, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('静态弹幕时长 $danmakuStaticDuration 秒'), - resetBtn( - theme, - 4.0, - () => updateStaticDuration(4.0), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 1, - max: 50, - value: danmakuStaticDuration, - divisions: 49, - label: danmakuStaticDuration.toString(), - onChanged: updateStaticDuration, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('弹幕行高 $danmakuLineHeight'), - resetBtn( - theme, - 1.6, - () => updateLineHeight(1.6), - ), - ], - ), - Padding( - padding: const EdgeInsets.only( - top: 0, - bottom: 6, - left: 10, - right: 10, - ), - child: SliderTheme( - data: sliderTheme, - child: Slider( - min: 1.0, - max: 3.0, - value: danmakuLineHeight, - onChanged: updateLineHeight, - onChangeEnd: (_) => - plPlayerController.putDanmakuSettings(), - ), - ), - ), - ], - ), - ), - ), - ); - }, - ); - } -} - class HeaderControl extends StatefulWidget { const HeaderControl({ required this.isPortrait, @@ -2298,9 +1668,9 @@ class HeaderControlState extends State bgColor: theme.colorScheme.surface, ), ), - ?_buildDanmakuList(ctr.staticDanmaku), - ?_buildDanmakuList(ctr.scrollDanmaku), - ?_buildDanmakuList(ctr.specialDanmaku), + ?_buildDanmakuList(ctr.staticDanmaku.nonNulls.toList()), + ?_buildDanmakuList(ctr.scrollDanmaku.expand((e) => e).toList()), + ?_buildDanmakuList(ctr.specialDanmaku.toList()), const SliverToBoxAdapter(child: SizedBox(height: 12)), ], ), @@ -2311,7 +1681,6 @@ class HeaderControlState extends State Widget? _buildDanmakuList(List> list) { if (list.isEmpty) return null; - list = List.of(list); return SliverList.builder( itemCount: list.length, @@ -2972,23 +2341,3 @@ class HeaderControlState extends State ); } } - -class MSliderTrackShape extends RoundedRectSliderTrackShape { - const MSliderTrackShape(); - - @override - Rect getPreferredRect({ - required RenderBox parentBox, - Offset offset = Offset.zero, - SliderThemeData? sliderTheme, - bool isEnabled = false, - bool isDiscrete = false, - }) { - const double trackHeight = 3; - final double trackLeft = offset.dx; - final double trackTop = - offset.dy + (parentBox.size.height - trackHeight) / 2 + 4; - final double trackWidth = parentBox.size.width; - return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); - } -} diff --git a/lib/pages/video/widgets/header_mixin.dart b/lib/pages/video/widgets/header_mixin.dart new file mode 100644 index 000000000..c5c0c1e24 --- /dev/null +++ b/lib/pages/video/widgets/header_mixin.dart @@ -0,0 +1,581 @@ +import 'package:PiliPlus/common/widgets/button/icon_button.dart'; +import 'package:PiliPlus/pages/setting/widgets/switch_item.dart'; +import 'package:PiliPlus/pages/video/introduction/ugc/widgets/menu_row.dart'; +import 'package:PiliPlus/plugin/pl_player/controller.dart'; +import 'package:PiliPlus/plugin/pl_player/utils/danmaku_options.dart'; +import 'package:PiliPlus/utils/extension/num_ext.dart'; +import 'package:PiliPlus/utils/page_utils.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +mixin HeaderMixin on State { + PlPlayerController get plPlayerController; + + bool get isFullScreen => plPlayerController.isFullScreen.value; + + Future? showBottomSheet( + StatefulWidgetBuilder builder, { + double? padding, + }) { + return PageUtils.showVideoBottomSheet( + context, + isFullScreen: () => isFullScreen, + padding: padding, + child: StatefulBuilder( + builder: (context, setState) => plPlayerController.darkVideoPage + ? Theme( + data: Theme.of(this.context), + child: builder(this.context, setState), + ) + : builder(context, setState), + ), + ); + } + + Widget resetBtn( + ThemeData theme, + Object def, + VoidCallback onPressed, { + bool isDanmaku = true, + }) { + return iconButton( + tooltip: '默认值: $def', + icon: const Icon(Icons.refresh), + onPressed: () { + onPressed(); + if (isDanmaku) { + // plPlayerController.putDanmakuSettings(); + } else { + plPlayerController.putSubtitleSettings(); + } + }, + iconColor: theme.colorScheme.outline, + size: 24, + iconSize: 24, + ); + } + + /// 弹幕功能 + Future showSetDanmaku({bool isLive = false}) async { + // 屏蔽类型 + const blockTypesList = [ + (value: 2, label: '滚动'), + (value: 5, label: '顶部'), + (value: 4, label: '底部'), + (value: 6, label: '彩色'), + (value: 7, label: '高级'), + ]; + + final danmakuController = plPlayerController.danmakuController; + + final isFullScreen = this.isFullScreen; + + await showBottomSheet( + (context, setState) { + final theme = Theme.of(context); + + void setOptions() => danmakuController?.updateOption( + DanmakuOptions.get( + notFullscreen: !isFullScreen, + speed: plPlayerController.playbackSpeed, + ), + ); + + final sliderTheme = SliderThemeData( + trackHeight: 10, + trackShape: const MSliderTrackShape(), + thumbColor: theme.colorScheme.primary, + activeTrackColor: theme.colorScheme.primary, + inactiveTrackColor: theme.colorScheme.onInverseSurface, + thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 6.0), + ); + + void updateLineHeight(double val) { + DanmakuOptions.danmakuLineHeight = val.toPrecision(1); + setState(() {}); + setOptions(); + } + + void updateDuration(double val) { + DanmakuOptions.danmakuDuration = val.toPrecision(1); + setState(() {}); + setOptions(); + } + + void updateStaticDuration(double val) { + DanmakuOptions.danmakuStaticDuration = val.toPrecision(1); + setState(() {}); + setOptions(); + } + + void updateFontSizeFS(double val) { + DanmakuOptions.danmakuFontScaleFS = val; + setState(() {}); + if (isFullScreen) { + setOptions(); + } + } + + void updateFontSize(double val) { + DanmakuOptions.danmakuFontScale = val; + setState(() {}); + if (!isFullScreen) { + setOptions(); + } + } + + void updateStrokeWidth(double val) { + DanmakuOptions.danmakuStrokeWidth = val; + setState(() {}); + setOptions(); + } + + void updateFontWeight(double val) { + DanmakuOptions.danmakuFontWeight = val.toInt(); + setOptions(); + } + + void updateOpacity(double val) { + plPlayerController.danmakuOpacity.value = val; + setState(() {}); + } + + void updateShowArea(double val) { + DanmakuOptions.danmakuShowArea = val.toPrecision(1); + setState(() {}); + setOptions(); + } + + void onUpdateBlockType(int blockType, bool blocked) { + if (blocked) { + DanmakuOptions.blockTypes.remove(blockType); + } else { + DanmakuOptions.blockTypes.add(blockType); + } + DanmakuOptions.blockColorful = DanmakuOptions.blockTypes.contains(6); + setState(() {}); + setOptions(); + } + + return Padding( + padding: const EdgeInsets.all(12), + child: Material( + clipBehavior: Clip.hardEdge, + color: theme.colorScheme.surface, + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: ListView( + padding: EdgeInsets.zero, + children: [ + const SizedBox( + height: 45, + child: Center( + child: Text('弹幕设置', style: TextStyle(fontSize: 14)), + ), + ), + const SizedBox(height: 10), + if (!isLive) ...[ + Row( + children: [ + Text('智能云屏蔽 ${plPlayerController.danmakuWeight} 级'), + const Spacer(), + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + onPressed: () => Get + ..back() + ..toNamed( + '/danmakuBlock', + arguments: plPlayerController, + ), + child: Text( + "屏蔽管理(${plPlayerController.filters.count})", + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0, + max: 10, + value: plPlayerController.danmakuWeight.toDouble(), + divisions: 10, + label: '${plPlayerController.danmakuWeight}', + onChanged: (val) => + plPlayerController.danmakuWeight = val.toInt(), + onChangeEnd: (val) => GStorage.setting.put( + SettingBoxKey.danmakuWeight, + val.toInt(), + ), + ), + ), + ), + ], + const Text('按类型屏蔽'), + Padding( + padding: const EdgeInsets.only(top: 12), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + spacing: 10, + children: blockTypesList.map( + (e) { + final blocked = DanmakuOptions.blockTypes.contains( + e.value, + ); + return ActionRowLineItem( + onTap: () => onUpdateBlockType(e.value, blocked), + text: e.label, + selectStatus: blocked, + ); + }, + ).toList(), + ), + ), + ), + SetSwitchItem( + title: '海量弹幕', + contentPadding: EdgeInsets.zero, + titleStyle: const TextStyle(fontSize: 14), + defaultVal: DanmakuOptions.massiveMode, + setKey: SettingBoxKey.danmakuMassiveMode, + onChanged: (value) { + DanmakuOptions.massiveMode = value; + setState(() {}); + setOptions(); + }, + ), + SetSwitchItem( + title: '滚动弹幕固定速度', + contentPadding: EdgeInsets.zero, + titleStyle: const TextStyle(fontSize: 14), + defaultVal: DanmakuOptions.scrollFixedVelocity, + setKey: SettingBoxKey.danmakuFixedV, + onChanged: (value) { + DanmakuOptions.scrollFixedVelocity = value; + setState(() {}); + setOptions(); + }, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('显示区域 ${DanmakuOptions.danmakuShowArea * 100}%'), + resetBtn( + theme, + '50.0%', + () => updateShowArea(0.5), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0.1, + max: 1, + value: DanmakuOptions.danmakuShowArea, + divisions: 9, + label: '${DanmakuOptions.danmakuShowArea * 100}%', + onChanged: updateShowArea, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('不透明度 ${plPlayerController.danmakuOpacity * 100}%'), + resetBtn( + theme, + '100.0%', + () => updateOpacity(1.0), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0, + max: 1, + value: plPlayerController.danmakuOpacity.value, + divisions: 10, + label: '${plPlayerController.danmakuOpacity * 100}%', + onChanged: updateOpacity, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '字体粗细 ${DanmakuOptions.danmakuFontWeight + 1}(可能无法精确调节)', + ), + resetBtn( + theme, + 6, + () => updateFontWeight(5), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0, + max: 8, + value: DanmakuOptions.danmakuFontWeight.toDouble(), + divisions: 8, + label: '${DanmakuOptions.danmakuFontWeight + 1}', + onChanged: updateFontWeight, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('描边粗细 ${DanmakuOptions.danmakuStrokeWidth}'), + resetBtn( + theme, + 1.5, + () => updateStrokeWidth(1.5), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0, + max: 5, + value: DanmakuOptions.danmakuStrokeWidth, + divisions: 10, + label: DanmakuOptions.danmakuStrokeWidth + .toStringAsFixed(0), + onChanged: updateStrokeWidth, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '字体大小 ${(DanmakuOptions.danmakuFontScale * 100).toStringAsFixed(1)}%', + ), + resetBtn( + theme, + '100.0%', + () => updateFontSize(1.0), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0.5, + max: 2.5, + value: DanmakuOptions.danmakuFontScale, + divisions: 20, + label: + '${(DanmakuOptions.danmakuFontScale * 100).toStringAsFixed(1)}%', + onChanged: updateFontSize, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '全屏字体大小 ${(DanmakuOptions.danmakuFontScaleFS * 100).toStringAsFixed(1)}%', + ), + resetBtn( + theme, + '120.0%', + () => updateFontSizeFS(1.2), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 0.5, + max: 2.5, + value: DanmakuOptions.danmakuFontScaleFS, + divisions: 20, + label: + '${(DanmakuOptions.danmakuFontScaleFS * 100).toStringAsFixed(1)}%', + onChanged: updateFontSizeFS, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('滚动弹幕时长 ${DanmakuOptions.danmakuDuration} 秒'), + resetBtn( + theme, + 7.0, + () => updateDuration(7.0), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 1, + max: 50, + value: DanmakuOptions.danmakuDuration, + divisions: 49, + label: DanmakuOptions.danmakuDuration.toString(), + onChanged: updateDuration, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('静态弹幕时长 ${DanmakuOptions.danmakuStaticDuration} 秒'), + resetBtn( + theme, + 4.0, + () => updateStaticDuration(4.0), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 1, + max: 50, + value: DanmakuOptions.danmakuStaticDuration, + divisions: 49, + label: DanmakuOptions.danmakuStaticDuration.toString(), + onChanged: updateStaticDuration, + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('弹幕行高 ${DanmakuOptions.danmakuLineHeight}'), + resetBtn( + theme, + 1.6, + () => updateLineHeight(1.6), + ), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: sliderTheme, + child: Slider( + min: 1.0, + max: 3.0, + value: DanmakuOptions.danmakuLineHeight, + onChanged: updateLineHeight, + ), + ), + ), + ], + ), + ), + ), + ); + }, + ); + + await DanmakuOptions.save(); + } +} + +class MSliderTrackShape extends RoundedRectSliderTrackShape { + const MSliderTrackShape(); + + @override + Rect getPreferredRect({ + required RenderBox parentBox, + Offset offset = Offset.zero, + SliderThemeData? sliderTheme, + bool isEnabled = false, + bool isDiscrete = false, + }) { + const double trackHeight = 3; + final double trackLeft = offset.dx; + final double trackTop = + offset.dy + (parentBox.size.height - trackHeight) / 2 + 4; + final double trackWidth = parentBox.size.width; + return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); + } +} diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 06d922238..b6e49ed03 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -321,7 +321,7 @@ class PlPlayerController { } } - /// 弹幕权重 + // 弹幕相关配置 late final enableTapDm = PlatformUtils.isMobile && Pref.enableTapDm; late int danmakuWeight = Pref.danmakuWeight; late RuleFilter filters = Pref.danmakuFilterRule; @@ -334,26 +334,16 @@ class PlPlayerController { ascii.encode(Accounts.main.mid.toString()), 0, ).toRadixString(16); - // 弹幕相关配置 - late Set blockTypes = Pref.danmakuBlockType; - late bool blockColorful = blockTypes.contains(6); - late double showArea = Pref.danmakuShowArea; - late RxDouble danmakuOpacity = Pref.danmakuOpacity.obs; - late double danmakuFontScale = Pref.danmakuFontScale; - late double danmakuFontScaleFS = Pref.danmakuFontScaleFS; - late double danmakuStrokeWidth = Pref.strokeWidth; - late int danmakuFontWeight = Pref.fontWeight; - late bool massiveMode = Pref.danmakuMassiveMode; - late double danmakuDuration = Pref.danmakuDuration; - late double danmakuStaticDuration = Pref.danmakuStaticDuration; + late final RxDouble danmakuOpacity = Pref.danmakuOpacity.obs; + late List speedList = Pref.speedList; late bool enableAutoLongPressSpeed = Pref.enableAutoLongPressSpeed; late final showControlDuration = Pref.enableLongShowControl ? const Duration(seconds: 30) : const Duration(seconds: 3); + // 字幕 late double subtitleFontScale = Pref.subtitleFontScale; late double subtitleFontScaleFS = Pref.subtitleFontScaleFS; - late double danmakuLineHeight = Pref.danmakuLineHeight; late int subtitlePaddingH = Pref.subtitlePaddingH; late int subtitlePaddingB = Pref.subtitlePaddingB; late double subtitleBgOpacity = Pref.subtitleBgOpacity; @@ -1676,22 +1666,6 @@ class PlPlayerController { video.put(VideoBoxKey.playRepeat, type.index); } - void putDanmakuSettings() { - setting.putAll({ - SettingBoxKey.danmakuWeight: danmakuWeight, - SettingBoxKey.danmakuBlockType: blockTypes.toList(), - SettingBoxKey.danmakuShowArea: showArea, - SettingBoxKey.danmakuOpacity: danmakuOpacity.value, - SettingBoxKey.danmakuFontScale: danmakuFontScale, - SettingBoxKey.danmakuFontScaleFS: danmakuFontScaleFS, - SettingBoxKey.danmakuDuration: danmakuDuration, - SettingBoxKey.danmakuStaticDuration: danmakuStaticDuration, - SettingBoxKey.strokeWidth: danmakuStrokeWidth, - SettingBoxKey.fontWeight: danmakuFontWeight, - SettingBoxKey.danmakuLineHeight: danmakuLineHeight, - }); - } - void putSubtitleSettings() { setting.putAll({ SettingBoxKey.subtitleFontScale: subtitleFontScale, diff --git a/lib/plugin/pl_player/utils/danmaku_options.dart b/lib/plugin/pl_player/utils/danmaku_options.dart new file mode 100644 index 000000000..09578453a --- /dev/null +++ b/lib/plugin/pl_player/utils/danmaku_options.dart @@ -0,0 +1,58 @@ +import 'package:PiliPlus/utils/extension/box_ext.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:canvas_danmaku/canvas_danmaku.dart'; + +abstract final class DanmakuOptions { + static final Set blockTypes = Pref.danmakuBlockType; + static bool blockColorful = blockTypes.contains(6); + + static double danmakuFontScaleFS = Pref.danmakuFontScaleFS; + static double danmakuFontScale = Pref.danmakuFontScale; + static int danmakuFontWeight = Pref.fontWeight; + static double danmakuShowArea = Pref.danmakuShowArea; + static double danmakuDuration = Pref.danmakuDuration; + static double danmakuStaticDuration = Pref.danmakuStaticDuration; + static double danmakuStrokeWidth = Pref.strokeWidth; + static bool scrollFixedVelocity = Pref.danmakuFixedV; + static bool massiveMode = Pref.danmakuMassiveMode; + static double danmakuLineHeight = Pref.danmakuLineHeight; + + static DanmakuOption get({ + required bool notFullscreen, + double speed = 1.0, + }) { + return DanmakuOption( + fontSize: notFullscreen ? danmakuFontScaleFS : danmakuFontScale, + fontWeight: danmakuFontWeight, + area: danmakuShowArea, + duration: danmakuDuration / speed, + staticDuration: danmakuStaticDuration / speed, + hideBottom: blockTypes.contains(5), + hideScroll: blockTypes.contains(2), + hideTop: blockTypes.contains(4), + hideSpecial: blockTypes.contains(7), + strokeWidth: danmakuStrokeWidth, + scrollFixedVelocity: scrollFixedVelocity, + massiveMode: massiveMode, + static2Scroll: true, + safeArea: true, + lineHeight: danmakuLineHeight, + ); + } + + static Future? save() { + return GStorage.setting.putAllNE({ + SettingBoxKey.danmakuBlockType: blockTypes.toList(), + SettingBoxKey.danmakuShowArea: danmakuShowArea, + SettingBoxKey.danmakuFontScale: danmakuFontScale, + SettingBoxKey.danmakuFontScaleFS: danmakuFontScaleFS, + SettingBoxKey.danmakuDuration: danmakuDuration, + SettingBoxKey.danmakuStaticDuration: danmakuStaticDuration, + SettingBoxKey.strokeWidth: danmakuStrokeWidth, + SettingBoxKey.fontWeight: danmakuFontWeight, + SettingBoxKey.danmakuLineHeight: danmakuLineHeight, + }); + } +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index abc8690a9..f6add645b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -1200,17 +1200,21 @@ class _PLVideoPlayerState extends State final ctr = plPlayerController.danmakuController; if (ctr != null) { final pos = details.localPosition; - final item = ctr.findSingleDanmaku(pos); - if (item == null) { + final res = ctr.findSingleDanmaku(pos); + if (res != null) { + final (dy, item) = res; + if (item != _suspendedDm) { + _suspendedDm?.suspend = false; + if (item.content.extra == null) { + _dmOffset.value = null; + return; + } + _suspendedDm = item..suspend = true; + this.dy = dy; + } + } else { _suspendedDm?.suspend = false; _dmOffset.value = null; - } else if (item != _suspendedDm) { - _suspendedDm?.suspend = false; - if (item.content.extra == null) { - _dmOffset.value = null; - return; - } - _suspendedDm = item..suspend = true; } } } @@ -2269,6 +2273,7 @@ class _PLVideoPlayerState extends State static const _actionItemHeight = 35.0 - _triangleHeight; DanmakuItem? _suspendedDm; + double dy = 0; late final Rxn _dmOffset = Rxn(); void _removeDmAction() { @@ -2327,9 +2332,6 @@ class _PLVideoPlayerState extends State final overlayWidth = _actionItemWidth * (seekOffset == null ? 3 : 4); - final dy = item.content.type == DanmakuItemType.bottom - ? maxHeight - item.yPosition - item.height - : item.yPosition; final top = dy + item.height + _triangleHeight + 2; final realLeft = dx + overlayWidth / 2; diff --git a/lib/utils/extension/box_ext.dart b/lib/utils/extension/box_ext.dart new file mode 100644 index 000000000..64fc6dba4 --- /dev/null +++ b/lib/utils/extension/box_ext.dart @@ -0,0 +1,28 @@ +import 'package:collection/collection.dart'; +import 'package:hive/hive.dart'; + +extension BoxExt on Box { + bool equal(dynamic key, E value) { + return const DeepCollectionEquality().equals(value, get(key)); + } + + Future? putNE(dynamic key, E value) { + if (!equal(key, value)) { + return put(key, value); + } + return null; + } + + Future? putAllNE(Map entries) { + final Map newEntries = {}; + entries.forEach((key, value) { + if (!equal(key, value)) { + newEntries[key] = value; + } + }); + if (newEntries.isNotEmpty) { + return putAll(newEntries); + } + return null; + } +} diff --git a/lib/utils/extension/num_ext.dart b/lib/utils/extension/num_ext.dart index 8396d0dc6..5cbf6a6d3 100644 --- a/lib/utils/extension/num_ext.dart +++ b/lib/utils/extension/num_ext.dart @@ -18,7 +18,7 @@ extension IntExt on int? { extension DoubleExt on double { double toPrecision(int fractionDigits) { - final mod = pow(10, fractionDigits.toDouble()).toDouble(); - return ((this * mod).round().toDouble() / mod); + final mod = pow(10, fractionDigits).toDouble(); + return (this * mod).roundToDouble() / mod; } } diff --git a/lib/utils/page_utils.dart b/lib/utils/page_utils.dart index 103c41de9..ed23feab7 100644 --- a/lib/utils/page_utils.dart +++ b/lib/utils/page_utils.dart @@ -643,16 +643,16 @@ abstract final class PageUtils { } } - static void showVideoBottomSheet( + static Future? showVideoBottomSheet( BuildContext context, { required Widget child, required ValueGetter isFullScreen, double? padding, }) { if (!context.mounted) { - return; + return null; } - Get.key.currentState!.push( + return Get.key.currentState!.push( PublishRoute( pageBuilder: (context, animation, secondaryAnimation) { if (context.isPortrait) { diff --git a/lib/utils/storage_key.dart b/lib/utils/storage_key.dart index 41f83d211..f260dab1a 100644 --- a/lib/utils/storage_key.dart +++ b/lib/utils/storage_key.dart @@ -191,6 +191,7 @@ abstract final class SettingBoxKey { danmakuDuration = 'danmakuDuration', danmakuStaticDuration = 'danmakuStaticDuration', danmakuMassiveMode = 'danmakuMassiveMode', + danmakuFixedV = 'danmakuFixedV', danmakuLineHeight = 'danmakuLineHeight', strokeWidth = 'strokeWidth', fontWeight = 'fontWeight'; diff --git a/lib/utils/storage_pref.dart b/lib/utils/storage_pref.dart index e65b03ce4..69966e8f0 100644 --- a/lib/utils/storage_pref.dart +++ b/lib/utils/storage_pref.dart @@ -319,6 +319,9 @@ abstract final class Pref { static bool get danmakuMassiveMode => _setting.get(SettingBoxKey.danmakuMassiveMode, defaultValue: false); + static bool get danmakuFixedV => + _setting.get(SettingBoxKey.danmakuFixedV, defaultValue: false); + static double get subtitleFontScale => _setting.get(SettingBoxKey.subtitleFontScale, defaultValue: 1.0); diff --git a/pubspec.lock b/pubspec.lock index 9b43aeea6..edc06c276 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -239,7 +239,7 @@ packages: description: path: "." ref: main - resolved-ref: "75704a77d287607d00742f7fd6e0fbccf8e89425" + resolved-ref: "1199a960662bf0549a60c0da51909628db041654" url: "https://github.com/bggRGjQaUbCoE/canvas_danmaku.git" source: git version: "0.2.6"