feat(ui): 도움말 다이얼로그 및 UI 개선
- HelpDialog 추가 - 게임 화면에 통계/도움말 버튼 추가 - CombatLog에 디버프 이벤트 표시 - AudioService mp3 확장자 지원 - 설정 텍스트 l10n 추가
This commit is contained in:
@@ -39,6 +39,10 @@ class MobileCarouselLayout extends StatefulWidget {
|
||||
this.specialAnimation,
|
||||
this.currentThemeMode = ThemeMode.system,
|
||||
this.onThemeModeChange,
|
||||
this.bgmVolume = 0.7,
|
||||
this.sfxVolume = 0.8,
|
||||
this.onBgmVolumeChange,
|
||||
this.onSfxVolumeChange,
|
||||
});
|
||||
|
||||
final GameState state;
|
||||
@@ -56,6 +60,18 @@ class MobileCarouselLayout extends StatefulWidget {
|
||||
final ThemeMode currentThemeMode;
|
||||
final void Function(ThemeMode mode)? onThemeModeChange;
|
||||
|
||||
/// BGM 볼륨 (0.0 ~ 1.0)
|
||||
final double bgmVolume;
|
||||
|
||||
/// SFX 볼륨 (0.0 ~ 1.0)
|
||||
final double sfxVolume;
|
||||
|
||||
/// BGM 볼륨 변경 콜백
|
||||
final void Function(double volume)? onBgmVolumeChange;
|
||||
|
||||
/// SFX 볼륨 변경 콜백
|
||||
final void Function(double volume)? onSfxVolumeChange;
|
||||
|
||||
@override
|
||||
State<MobileCarouselLayout> createState() => _MobileCarouselLayoutState();
|
||||
}
|
||||
@@ -200,6 +216,108 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 사운드 상태 텍스트 가져오기
|
||||
String _getSoundStatus() {
|
||||
final bgmPercent = (widget.bgmVolume * 100).round();
|
||||
final sfxPercent = (widget.sfxVolume * 100).round();
|
||||
if (bgmPercent == 0 && sfxPercent == 0) {
|
||||
return l10n.uiSoundOff;
|
||||
}
|
||||
return 'BGM $bgmPercent% / SFX $sfxPercent%';
|
||||
}
|
||||
|
||||
/// 사운드 설정 다이얼로그 표시
|
||||
void _showSoundDialog(BuildContext context) {
|
||||
// StatefulBuilder를 사용하여 다이얼로그 내 상태 관리
|
||||
var bgmVolume = widget.bgmVolume;
|
||||
var sfxVolume = widget.sfxVolume;
|
||||
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setDialogState) => AlertDialog(
|
||||
title: Text(l10n.uiSound),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// BGM 볼륨
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
bgmVolume == 0 ? Icons.music_off : Icons.music_note,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(l10n.uiBgmVolume),
|
||||
Text('${(bgmVolume * 100).round()}%'),
|
||||
],
|
||||
),
|
||||
Slider(
|
||||
value: bgmVolume,
|
||||
onChanged: (value) {
|
||||
setDialogState(() => bgmVolume = value);
|
||||
widget.onBgmVolumeChange?.call(value);
|
||||
},
|
||||
divisions: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// SFX 볼륨
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
sfxVolume == 0 ? Icons.volume_off : Icons.volume_up,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(l10n.uiSfxVolume),
|
||||
Text('${(sfxVolume * 100).round()}%'),
|
||||
],
|
||||
),
|
||||
Slider(
|
||||
value: sfxVolume,
|
||||
onChanged: (value) {
|
||||
setDialogState(() => sfxVolume = value);
|
||||
widget.onSfxVolumeChange?.call(value);
|
||||
},
|
||||
divisions: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(l10n.buttonConfirm),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 세이브 삭제 확인 다이얼로그 표시
|
||||
void _showDeleteConfirmDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
@@ -324,6 +442,27 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
||||
},
|
||||
),
|
||||
|
||||
// 사운드 설정
|
||||
if (widget.onBgmVolumeChange != null ||
|
||||
widget.onSfxVolumeChange != null)
|
||||
ListTile(
|
||||
leading: Icon(
|
||||
widget.bgmVolume == 0 && widget.sfxVolume == 0
|
||||
? Icons.volume_off
|
||||
: Icons.volume_up,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
title: Text(l10n.uiSound),
|
||||
trailing: Text(
|
||||
_getSoundStatus(),
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
_showSoundDialog(context);
|
||||
},
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// 저장
|
||||
@@ -381,7 +520,7 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
|
||||
actions: [
|
||||
// 옵션 버튼
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => _showOptionsMenu(context),
|
||||
tooltip: l10n.menuOptions,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user