235 lines
12 KiB
Dart
235 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../../app/app_controller.dart';
|
||
import '../../services/app_shell.dart';
|
||
import '../update_dialog.dart';
|
||
import '../../app/settings.dart';
|
||
import '../../platform/android/android_shell.dart' show applyScreenOrientation;
|
||
import '../language_settings.dart';
|
||
import '../theme.dart';
|
||
import '../../l10n/tr.dart';
|
||
import '../setting_tile.dart';
|
||
|
||
// 약속 (설정 B): 환경 설정의 분야 - 일반 (언어 · 알림 · 앱 아이콘) · 화면. 바꾸면 바로 설정에 저장 (c.updateSettings)
|
||
|
||
/// 환경 설정 > 일반
|
||
class GeneralSettingsSection extends StatefulWidget {
|
||
const GeneralSettingsSection({super.key, required this.c, required this.desk});
|
||
|
||
final AppController c;
|
||
|
||
/// PC (Android 가 아님) - 창 · 트레이 · 탐색기 · 다운로드 · 내장 Chrome 설정
|
||
final bool desk;
|
||
|
||
@override
|
||
State<GeneralSettingsSection> createState() => _GeneralSettingsSectionState();
|
||
}
|
||
|
||
class _GeneralSettingsSectionState extends State<GeneralSettingsSection> {
|
||
AppController get c => widget.c;
|
||
bool get desk => widget.desk;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final s = c.settings;
|
||
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [
|
||
// 화면 언어 (읽을 수 없는 언어를 골라도 바로 찾아 되돌릴 수 있게 맨 위)
|
||
const LanguageSettings(),
|
||
// 102: 물어 정한 것 · 1차 판단은 모두 여기서 바꿀 수 있다 (기본값 = 그 답)
|
||
SwitchListTile(
|
||
value: s.migrationNotice,
|
||
onChanged: (v) => c.updateSettings((x) => x.migrationNotice = v),
|
||
title: Text(tr('업데이트로 바뀐 기본값 알림')),
|
||
subtitle: Text(tr('새 버전이 예전 기본값을 바꿨으면 켤 때 한 번 무엇이 바뀌었는지 알려 줍니다')),
|
||
),
|
||
SwitchListTile(
|
||
value: s.rememberPasswords,
|
||
onChanged: (v) => c.updateSettings((x) => x.rememberPasswords = v),
|
||
title: Text(tr('비밀번호 기억 (안전 저장소)')),
|
||
subtitle: Text(tr('WebDAV · OpenSubtitles 비밀번호를 이 기기의 안전 저장소에 둡니다. 끄면 저장하지 않고 (있던 것도 지움) 앱을 켤 때마다 다시 넣습니다')),
|
||
),
|
||
AppIconTile(c: c, desk: desk),
|
||
if (c.services.updater != null) ...[
|
||
SettingTile(
|
||
leading: Icon(Icons.system_update_alt, color: JjColors.accent),
|
||
title: FutureBuilder<String>(
|
||
future: c.services.updater!.currentVersion(),
|
||
builder: (_, v) => Text('JJ_MKVMaker v${v.data ?? '…'}'),
|
||
),
|
||
subtitle: Text(tr('GitHub 에 올려 둔 모든 버전 중에서 골라 설치합니다 (새 버전으로 · 예전 정상 버전으로 되돌리기). '
|
||
'바꾸기 전 버전의 설정은 보관해 두었다가, 그 버전으로 돌아오면 되살립니다')),
|
||
isThreeLine: true,
|
||
trailing: Wrap(spacing: 8, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||
UpdateCheckButton(c: c),
|
||
FilledButton.icon(
|
||
onPressed: () => chooseVersion(context, c),
|
||
icon: const Icon(Icons.system_update_alt, size: 18),
|
||
label: Text(tr('업데이트 · 버전 고르기')),
|
||
),
|
||
]),
|
||
),
|
||
SwitchListTile(
|
||
value: s.autoCheckUpdates,
|
||
onChanged: (v) => c.updateSettings((x) => x.autoCheckUpdates = v),
|
||
title: Text(tr('시작할 때 새 버전 확인 (하루 한 번)')),
|
||
),
|
||
],
|
||
SettingTile(
|
||
title: Text(tr('홈 화면')),
|
||
subtitle: Text(tr('프로그램을 켰을 때, 그리고 위쪽 왼쪽 JJ 아이콘을 눌렀을 때 보일 화면')),
|
||
trailing: DropdownButton<String>(
|
||
value: s.startScreen,
|
||
items: [
|
||
DropdownMenuItem(value: 'home', child: Text(tr('MKV 화면 (기본)'))),
|
||
DropdownMenuItem(value: 'browser', child: Text(tr('웹 브라우저 (홈 주소)'))),
|
||
DropdownMenuItem(value: 'files', child: Text(tr('파일 탐색기'))),
|
||
if (s.components.contains('tor') || s.startScreen == 'tor')
|
||
DropdownMenuItem(value: 'tor', child: Text(tr('Tor 브라우저'))),
|
||
],
|
||
onChanged: (v) => c.updateSettings((x) => x.startScreen = v!),
|
||
),
|
||
),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/// 환경 설정 > 화면
|
||
class DisplaySettingsSection extends StatefulWidget {
|
||
const DisplaySettingsSection({super.key, required this.c, required this.desk});
|
||
|
||
final AppController c;
|
||
|
||
/// PC (Android 가 아님) - 창 · 트레이 · 탐색기 · 다운로드 · 내장 Chrome 설정
|
||
final bool desk;
|
||
|
||
@override
|
||
State<DisplaySettingsSection> createState() => _DisplaySettingsSectionState();
|
||
}
|
||
|
||
class _DisplaySettingsSectionState extends State<DisplaySettingsSection> {
|
||
AppController get c => widget.c;
|
||
bool get desk => widget.desk;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final s = c.settings;
|
||
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [
|
||
if (!desk)
|
||
SettingTile(
|
||
title: Text(tr('화면 방향')),
|
||
subtitle: Text(tr('가로 고정 · 세로 고정, 또는 자동 (기기를 돌리는 대로, 기기의 자동 회전 설정을 따름)')),
|
||
trailing: DropdownButton<String>(
|
||
value: s.screenOrientation,
|
||
items: [
|
||
DropdownMenuItem(value: 'auto', child: Text(tr('자동 (기본)'))),
|
||
DropdownMenuItem(value: 'landscape', child: Text(tr('가로 고정'))),
|
||
DropdownMenuItem(value: 'portrait', child: Text(tr('세로 고정'))),
|
||
],
|
||
onChanged: (v) {
|
||
c.updateSettings((x) => x.screenOrientation = v!);
|
||
applyScreenOrientation(v!);
|
||
},
|
||
),
|
||
),
|
||
SettingTile(
|
||
title: Text(tr('기본 화면 크기')),
|
||
subtitle: Text(trf('글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 ' '(지금 {0}%)', [(s.uiScale * 100).round()])),
|
||
trailing: DropdownButton<double>(
|
||
value: s.uiScaleDefault,
|
||
items: [
|
||
for (var v = AppSettings.uiScaleMin; v <= AppSettings.uiScaleMax + 0.001; v += 0.1)
|
||
DropdownMenuItem(
|
||
value: AppSettings.clampUiScale(v),
|
||
child: Text('${(v * 100).round()}%${(v - 1).abs() < 0.001 ? tr(' (보통)') : ''}')),
|
||
// 5% 단위로 저장된 값이 목록에 없을 때
|
||
if (((s.uiScaleDefault * 100).round() % 10) != 0)
|
||
DropdownMenuItem(
|
||
value: s.uiScaleDefault, child: Text('${(s.uiScaleDefault * 100).round()}%')),
|
||
],
|
||
// 기본 크기를 바꾸면 지금 크기도 그 크기로
|
||
onChanged: (v) => c.updateSettings((x) => x
|
||
..uiScaleDefault = v!
|
||
..uiScale = v),
|
||
),
|
||
),
|
||
// X-plore 비교 5-1 ⑦ (관리자 지시 10/21): 화면 밝기 - 처음 값 어둡게 (예전 그대로), 다 끝날 때까지 "시험"
|
||
SettingTile(
|
||
title: Row(mainAxisSize: MainAxisSize.min, children: [
|
||
Flexible(child: Text(tr('화면 밝기'))),
|
||
const SizedBox(width: 6),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
|
||
decoration: BoxDecoration(border: Border.all(color: JjColors.warning), borderRadius: BorderRadius.circular(4)),
|
||
child: Text(tr('시험'), style: TextStyle(fontSize: 11, color: JjColors.warning)),
|
||
),
|
||
]),
|
||
subtitle: Text(tr('밝은 화면 · 기기 따라는 아직 시험 중입니다 - 일부 화면의 색이 덜 맞을 수 있습니다')),
|
||
trailing: DropdownButton<String>(
|
||
value: s.uiBrightness,
|
||
items: [
|
||
DropdownMenuItem(value: 'dark', child: Text(tr('어두운 화면'))),
|
||
DropdownMenuItem(value: 'light', child: Text(tr('밝은 화면'))),
|
||
DropdownMenuItem(value: 'system', child: Text(tr('기기 따라'))),
|
||
],
|
||
onChanged: (v) => c.updateSettings((x) => x.uiBrightness = v!),
|
||
),
|
||
),
|
||
]);
|
||
}
|
||
}
|
||
|
||
/// 앱 아이콘 고르기: 그림을 누르면 바로 바뀐다
|
||
class AppIconTile extends StatelessWidget {
|
||
const AppIconTile({super.key, required this.c, required this.desk});
|
||
|
||
final AppController c;
|
||
final bool desk;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final now = appIconOf(c.settings.appIcon);
|
||
return SettingTile(
|
||
title: Text(tr('앱 아이콘')),
|
||
subtitle: Padding(
|
||
padding: const EdgeInsets.only(top: 4),
|
||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||
Text(desk
|
||
? tr('창 · 작업 표시줄 · 트레이와 앱 안 왼쪽 위 버튼의 아이콘입니다 (실행 파일 · 바탕 화면 바로 가기 아이콘은 그대로)')
|
||
: tr('앱 목록 · 홈 화면과 앱 안 왼쪽 위 버튼의 아이콘입니다. 바꾸면 홈 화면에 둔 아이콘이 없어질 수 있어 앱 목록에서 다시 끌어다 놓아야 할 수 있습니다')),
|
||
const SizedBox(height: 8),
|
||
Wrap(spacing: 10, runSpacing: 10, children: [
|
||
for (final id in appIconIds)
|
||
Tooltip(
|
||
message: id == appIconIds.first ? '${_name(id)} (${tr('기본')})' : _name(id),
|
||
child: InkWell(
|
||
borderRadius: BorderRadius.circular(14),
|
||
onTap: id == now
|
||
? null
|
||
: () {
|
||
c.updateSettings((x) => x.appIcon = id);
|
||
c.services.shell.setAppIcon(id);
|
||
},
|
||
child: Container(
|
||
padding: const EdgeInsets.all(4),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(14),
|
||
border: Border.all(color: id == now ? JjColors.accent : Colors.transparent, width: 3),
|
||
),
|
||
child: Image.asset(appIconPreviewAsset(id), width: 56, height: 56, filterQuality: FilterQuality.medium),
|
||
),
|
||
),
|
||
),
|
||
]),
|
||
]),
|
||
),
|
||
);
|
||
}
|
||
|
||
static String _name(String id) => switch (id) {
|
||
'yellow' => tr('노랑'),
|
||
'black' => tr('검정'),
|
||
'film_jj' => tr('필름 + JJ'),
|
||
'film' => tr('필름'),
|
||
_ => tr('처음 아이콘 (파랑)'),
|
||
};
|
||
}
|