Initial commit
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
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)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user