Files
2026-07-14 11:11:36 +08:00

217 lines
7.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:forui/forui.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/contracts/danmaku.dart';
import '../../../providers/danmaku_settings_provider.dart';
import '../../../shared/widgets/setting_select.dart';
import '../utils/settings_labels.dart';
import '../widgets/blocked_keywords_dialog.dart';
import '../widgets/danmaku_sources_dialog.dart';
import '../widgets/settings_card.dart';
import '../widgets/settings_stepper_row.dart';
class DanmakuSettingsTab extends ConsumerWidget {
const DanmakuSettingsTab({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final danmakuSettings = ref.watch(danmakuSettingsProvider);
return SettingsTabScroll(
children: [
danmakuSettings.when(
data: (data) => Column(
children: [
SettingsCard(
children: [
SettingRow(
title: '全局启用弹幕',
trailing: FSwitch(
value: data.enabled,
onChange: (v) => ref
.read(danmakuSettingsProvider.notifier)
.setEnabled(v),
),
),
],
),
if (data.enabled) ...[
_buildDanmakuDisplayCard(context, ref, data.panelSettings),
SettingsCard(
children: [
SettingRow(
title: '简繁转换',
subtitle: '仅对兼容原版 dandanplay 的源生效',
trailing: SettingSelect<ChineseConvertMode>(
value: data.chineseConvert,
options: ChineseConvertMode.values,
labelOf: chineseConvertLabel,
onChanged: (v) {
if (v != null) {
ref
.read(danmakuSettingsProvider.notifier)
.setChineseConvert(v);
}
},
),
),
SettingRow(
title: '弹幕源',
subtitle: danmakuSourcesSummary(data.sources),
trailing: const Icon(Icons.chevron_right, size: 18),
onTap: () =>
showDanmakuSourcesDialog(context, data.sources),
),
SettingRow(
title: '合并重复弹幕',
trailing: FSwitch(
value: data.mergeDuplicates,
onChange: (v) => ref
.read(danmakuSettingsProvider.notifier)
.setMergeDuplicates(v),
),
),
SettingRow(
title: '屏蔽关键词',
subtitle: data.blockedKeywords.isEmpty
? '未设置'
: '${data.blockedKeywords.length}',
trailing: const Icon(Icons.chevron_right, size: 18),
onTap: () => showBlockedKeywordsDialog(
context,
ref,
data.blockedKeywords,
),
),
],
),
],
],
),
loading: () => const SizedBox.shrink(),
error: (e, _) => settingsLoadError(e),
),
],
);
}
Widget _buildDanmakuDisplayCard(
BuildContext context,
WidgetRef ref,
DanmakuPanelSettings p,
) {
void emit(DanmakuPanelSettings next) =>
ref.read(danmakuSettingsProvider.notifier).updatePanelSettings(next);
double inc(double v, double step, double min, double max) =>
double.parse((v + step).clamp(min, max).toStringAsFixed(2));
double dec(double v, double step, double min, double max) =>
double.parse((v - step).clamp(min, max).toStringAsFixed(2));
String opacityStr(double v) =>
(v * 10).round() == 10 ? '1.0' : '0.${(v * 10).round()}';
return Column(
children: [
SettingsCard(
children: [
StepperRow(
title: '字号大小',
display: p.fontSize.round().toString(),
onDecrement: p.fontSize > 12
? () => emit(p.copyWith(fontSize: dec(p.fontSize, 2, 12, 40)))
: null,
onIncrement: p.fontSize < 40
? () => emit(p.copyWith(fontSize: inc(p.fontSize, 2, 12, 40)))
: null,
),
StepperRow(
title: '基础速度',
display: '${p.speed.toStringAsFixed(2)}x',
onDecrement: p.speed > 0.25
? () => emit(p.copyWith(speed: dec(p.speed, 0.25, 0.25, 2.0)))
: null,
onIncrement: p.speed < 2.0
? () => emit(p.copyWith(speed: inc(p.speed, 0.25, 0.25, 2.0)))
: null,
),
StepperRow(
title: '不透明度',
display: opacityStr(p.opacity),
onDecrement: p.opacity > 0.1
? () =>
emit(p.copyWith(opacity: dec(p.opacity, 0.1, 0.1, 1.0)))
: null,
onIncrement: p.opacity < 1.0
? () =>
emit(p.copyWith(opacity: inc(p.opacity, 0.1, 0.1, 1.0)))
: null,
),
StepperRow(
title: '描边大小',
display: p.strokeWidth.toStringAsFixed(1),
onDecrement: p.strokeWidth > 0.0
? () => emit(
p.copyWith(
strokeWidth: dec(p.strokeWidth, 0.5, 0.0, 3.0),
),
)
: null,
onIncrement: p.strokeWidth < 3.0
? () => emit(
p.copyWith(
strokeWidth: inc(p.strokeWidth, 0.5, 0.0, 3.0),
),
)
: null,
),
StepperRow(
title: '行间距',
display: p.lineHeight.toStringAsFixed(1),
onDecrement: p.lineHeight > 1.0
? () => emit(
p.copyWith(lineHeight: dec(p.lineHeight, 0.1, 1.0, 3.0)),
)
: null,
onIncrement: p.lineHeight < 3.0
? () => emit(
p.copyWith(lineHeight: inc(p.lineHeight, 0.1, 1.0, 3.0)),
)
: null,
),
StepperRow(
title: '弹幕行数',
display: p.maxRows == 0 ? '自动' : '${p.maxRows}',
onDecrement: p.maxRows > 0
? () => emit(p.copyWith(maxRows: p.maxRows - 1))
: null,
onIncrement: p.maxRows < 10
? () => emit(p.copyWith(maxRows: p.maxRows + 1))
: null,
),
],
),
SettingsCard(
children: [
SettingRow(
title: '粗体',
trailing: FSwitch(
value: p.bold,
onChange: (v) => emit(p.copyWith(bold: v)),
),
),
SettingRow(
title: '固定弹幕转滚动',
trailing: FSwitch(
value: p.fixedToScroll,
onChange: (v) => emit(p.copyWith(fixedToScroll: v)),
),
),
],
),
],
);
}
}