From ac1e655a6552d0101895892b0363ec8acc17f757 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Tue, 29 Oct 2024 12:17:22 +0800 Subject: [PATCH] feat: sponsorblock: custom color --- lib/pages/setting/sponsor_block_page.dart | 208 +++++++++++++++++++++- lib/pages/video/detail/controller.dart | 4 +- lib/utils/storage.dart | 17 +- 3 files changed, 226 insertions(+), 3 deletions(-) diff --git a/lib/pages/setting/sponsor_block_page.dart b/lib/pages/setting/sponsor_block_page.dart index 47820c5d..3a7c2915 100644 --- a/lib/pages/setting/sponsor_block_page.dart +++ b/lib/pages/setting/sponsor_block_page.dart @@ -6,6 +6,7 @@ import 'package:PiliPalaX/pages/video/detail/controller.dart' import 'package:PiliPalaX/utils/storage.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; class SponsorBlockPage extends StatefulWidget { @@ -18,6 +19,7 @@ class SponsorBlockPage extends StatefulWidget { class _SponsorBlockPageState extends State { late double _blockLimit; late List> _blockSettings; + late List _blockColor; final _url = 'https://github.com/hanydd/BilibiliSponsorBlock'; @override @@ -25,6 +27,7 @@ class _SponsorBlockPageState extends State { super.initState(); _blockLimit = GStorage.blockLimit; _blockSettings = GStorage.blockSettings; + _blockColor = GStorage.blockColor; } @override @@ -101,6 +104,37 @@ class _SponsorBlockPageState extends State { ), ) : ListTile( + enabled: + _blockSettings[index - 1].second != SkipType.disable, + onTap: () { + showDialog( + context: context, + builder: (_) => AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: + const EdgeInsets.symmetric(vertical: 16), + title: const Text( + 'Color Picker', + style: TextStyle(fontSize: 18), + ), + content: SlideColorPicker( + color: _blockColor[index - 1], + callback: (Color? color) async { + _blockColor[index - 1] = color ?? + _blockSettings[index - 1].first.color; + await GStorage.setting.put( + SettingBoxKey.blockColor, + _blockColor + .map((item) => item.value + .toRadixString(16) + .substring(2)) + .toList()); + setState(() {}); + }, + ), + ), + ); + }, leading: Container( height: 24, width: 24, @@ -110,7 +144,7 @@ class _SponsorBlockPageState extends State { width: 10, decoration: BoxDecoration( shape: BoxShape.circle, - color: _blockSettings[index - 1].first.color, + color: _blockColor[index - 1], ), ), ), @@ -171,3 +205,175 @@ class _SponsorBlockPageState extends State { ); } } + +class SlideColorPicker extends StatefulWidget { + const SlideColorPicker({ + super.key, + required this.color, + required this.callback, + }); + + final Color color; + final Function(Color? color) callback; + + @override + State createState() => _SlideColorPickerState(); +} + +class _SlideColorPickerState extends State { + late int _r; + late int _g; + late int _b; + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _r = widget.color.red; + _g = widget.color.green; + _b = widget.color.blue; + _textController = TextEditingController(text: _convert); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } + + String get _convert => Color.fromARGB(255, _r, _g, _b) + .value + .toRadixString(16) + .substring(2) + .toUpperCase(); + + Widget _slider({ + required String title, + required int value, + required ValueChanged onChanged, + }) { + return Row( + children: [ + const SizedBox(width: 16), + Text(title), + Expanded( + child: Slider( + min: 0, + max: 255, + divisions: 255, + value: value.toDouble(), + onChanged: onChanged, + ), + ), + Text( + value.toString(), + ), + const SizedBox(width: 16), + ], + ); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + height: 100, + color: Color.fromARGB(255, _r, _g, _b), + ), + const SizedBox(height: 10), + IntrinsicWidth( + child: TextField( + inputFormatters: [ + LengthLimitingTextInputFormatter(6), + FilteringTextInputFormatter.allow(RegExp('[0-9a-fA-F]')), + ], + controller: _textController, + decoration: InputDecoration( + isDense: true, + prefixText: '#', + contentPadding: const EdgeInsets.all(0), + ), + onChanged: (value) { + _textController.text = value.toUpperCase(); + if (value.length == 6) { + Color color = + Color(int.tryParse('FF$value', radix: 16) ?? 0xFF000000); + setState(() { + _r = color.red; + _g = color.green; + _b = color.blue; + }); + } + }, + ), + ), + _slider( + title: 'R', + value: _r, + onChanged: (value) { + setState(() { + _r = value.round(); + _textController.text = _convert; + }); + }, + ), + _slider( + title: 'G', + value: _g, + onChanged: (value) { + setState(() { + _g = value.round(); + _textController.text = _convert; + }); + }, + ), + _slider( + title: 'B', + value: _b, + onChanged: (value) { + setState(() { + _b = value.round(); + _textController.text = _convert; + }); + }, + ), + Row( + children: [ + const SizedBox(width: 16), + TextButton( + onPressed: () { + Get.back(); + widget.callback(null); + }, + child: Text( + '重置', + ), + ), + const Spacer(), + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + widget.callback(Color.fromARGB(255, _r, _g, _b)); + }, + child: const Text('确定'), + ), + const SizedBox(width: 16), + ], + ) + ], + ), + ); + } +} diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 97273095..4601e27a 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -207,12 +207,14 @@ class VideoDetailController extends GetxController if (_enableSponsorBlock) { _blockLimit = GStorage.blockLimit; _blockSettings = GStorage.blockSettings; + _blockColor = GStorage.blockColor; } } int? _lastPos; double? _blockLimit; List>? _blockSettings; + List? _blockColor; List? _segmentList; List? _segmentProgressList; @@ -276,7 +278,7 @@ class VideoDetailController extends GetxController return Segment( start, start == end ? (end + 0.01).clamp(0.0, 1.0) : end, - item.segmentType.color, + _blockColor?[item.segmentType.index] ?? item.segmentType.color, ); }).toList(); } catch (e) { diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 78bd4881..270ac234 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -2,9 +2,10 @@ import 'dart:convert'; import 'dart:io'; import 'dart:ui'; import 'package:PiliPalaX/common/widgets/pair.dart'; +import 'package:PiliPalaX/main.dart'; import 'package:PiliPalaX/models/common/theme_type.dart'; import 'package:PiliPalaX/pages/video/detail/controller.dart' - show SegmentType, SkipType; + show SegmentType, SegmentTypeExt, SkipType; import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; @@ -33,6 +34,19 @@ class GStorage { .toList(); } + static List get blockColor { + List list = setting.get( + SettingBoxKey.blockColor, + defaultValue: List.generate(SegmentType.values.length, (_) => ''), + ); + return SegmentType.values + .map((item) => list[item.index].isNotEmpty + ? Color( + int.tryParse('FF${list[item.index]}', radix: 16) ?? 0xFF000000) + : item.color) + .toList(); + } + static double get blockLimit => setting.get(SettingBoxKey.blockLimit, defaultValue: 0.0); @@ -210,6 +224,7 @@ class SettingBoxKey { enableSponsorBlock = 'enableSponsorBlock', blockSettings = 'blockSettings', blockLimit = 'blockLimit', + blockColor = 'blockColor', // 弹幕相关设置 权重(云屏蔽) 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 字体粗细 danmakuWeight = 'danmakuWeight',