правки по лаунчеру
This commit is contained in:
@@ -99,7 +99,7 @@ class MwGameTile extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!enabled)
|
||||
if (!enabled && subtitle == null)
|
||||
const Text(
|
||||
'SOON',
|
||||
style: TextStyle(
|
||||
@@ -121,13 +121,24 @@ class MwGameTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
class MwGameRail extends StatelessWidget {
|
||||
const MwGameRail({super.key, this.compact = false});
|
||||
const MwGameRail({
|
||||
super.key,
|
||||
this.compact = false,
|
||||
this.onSettings,
|
||||
this.username,
|
||||
});
|
||||
|
||||
final bool compact;
|
||||
final VoidCallback? onSettings;
|
||||
final String? username;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
final normalizedUsername = username?.trim() ?? '';
|
||||
final userInitial = normalizedUsername.isEmpty
|
||||
? '?'
|
||||
: normalizedUsername.characters.first.toUpperCase();
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(14, 48, 14, 30),
|
||||
decoration: const BoxDecoration(
|
||||
@@ -154,7 +165,10 @@ class MwGameRail extends StatelessWidget {
|
||||
BoxShadow(color: accent.withAlpha(70), blurRadius: 12),
|
||||
],
|
||||
),
|
||||
child: const Text('A', style: TextStyle(fontFamily: 'Cinzel')),
|
||||
child: Text(
|
||||
userInitial,
|
||||
style: const TextStyle(fontFamily: 'Cinzel'),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const Divider(color: _border),
|
||||
@@ -183,28 +197,13 @@ class MwGameRail extends StatelessWidget {
|
||||
onPressed: () {},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const MwGameTile(
|
||||
MwGameTile(
|
||||
title: 'MoonWell Karts',
|
||||
subtitle: 'Closed Alpha',
|
||||
subtitle: 'В разработке',
|
||||
mark: 'K',
|
||||
icon: Icons.sports_motorsports,
|
||||
enabled: false,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const MwGameTile(
|
||||
title: 'Tides of Elune',
|
||||
subtitle: 'В разработке',
|
||||
mark: 'T',
|
||||
icon: Icons.style,
|
||||
enabled: false,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const MwGameTile(
|
||||
title: 'Project Verge',
|
||||
subtitle: 'Анонс',
|
||||
mark: 'V',
|
||||
icon: Icons.auto_awesome,
|
||||
enabled: false,
|
||||
compact: compact,
|
||||
),
|
||||
const Spacer(),
|
||||
const Divider(color: _border),
|
||||
@@ -216,12 +215,15 @@ class MwGameRail extends StatelessWidget {
|
||||
icon: const Icon(Icons.people_outline, size: 18),
|
||||
),
|
||||
const Spacer(),
|
||||
Icon(
|
||||
Icons.settings_outlined,
|
||||
color: accent.withAlpha(150),
|
||||
size: 18,
|
||||
IconButton(
|
||||
onPressed: onSettings,
|
||||
tooltip: 'Настройки',
|
||||
icon: Icon(
|
||||
Icons.settings_outlined,
|
||||
color: accent.withAlpha(150),
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -236,11 +238,13 @@ class MwNewsItemData {
|
||||
required this.body,
|
||||
this.createdAt,
|
||||
this.imageUrl,
|
||||
this.onPressed,
|
||||
});
|
||||
final String title;
|
||||
final String body;
|
||||
final DateTime? createdAt;
|
||||
final String? imageUrl;
|
||||
final VoidCallback? onPressed;
|
||||
}
|
||||
|
||||
class MwNewsItem extends StatelessWidget {
|
||||
@@ -251,55 +255,64 @@ class MwNewsItem extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
final date = item.createdAt?.toLocal();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 13, 12, 13),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: item.onPressed,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 13, 12, 13),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: accent),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
'НОВОСТЬ',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: accent,
|
||||
fontSize: 8,
|
||||
letterSpacing: 1.5,
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: accent),
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
),
|
||||
child: Text(
|
||||
'НОВОСТЬ',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: accent,
|
||||
fontSize: 8,
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
date == null
|
||||
? 'СЕГОДНЯ'
|
||||
: '${date.day.toString().padLeft(2, '0')}.${date.month.toString().padLeft(2, '0')}',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 9,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(height: 7),
|
||||
Text(
|
||||
date == null
|
||||
? 'СЕГОДНЯ'
|
||||
: '${date.day.toString().padLeft(2, '0')}.${date.month.toString().padLeft(2, '0')}',
|
||||
item.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 9,
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
color: _ivory,
|
||||
fontSize: 16,
|
||||
height: 1.25,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
Text(
|
||||
item.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
color: _ivory,
|
||||
fontSize: 16,
|
||||
height: 1.25,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -337,8 +350,6 @@ class MwNewsList extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
_NewsTab(label: 'НОВОСТИ', selected: true, accent: accent),
|
||||
_NewsTab(label: 'СОБЫТИЯ', accent: accent),
|
||||
_NewsTab(label: 'ПАТЧИ', accent: accent),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -353,7 +364,7 @@ class MwNewsList extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
const Text(
|
||||
'MOONWELL.GG',
|
||||
'MOON-WELL.ONLINE',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
@@ -455,6 +466,9 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
this.progress,
|
||||
this.currentPath,
|
||||
this.error,
|
||||
this.realmName,
|
||||
this.realmOnline,
|
||||
this.realmGameBuild,
|
||||
});
|
||||
final MwSyncPresentation state;
|
||||
final String status;
|
||||
@@ -463,6 +477,9 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
final double? progress;
|
||||
final String? currentPath;
|
||||
final String? error;
|
||||
final String? realmName;
|
||||
final bool? realmOnline;
|
||||
final int? realmGameBuild;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -477,6 +494,14 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
final value = ready || state == MwSyncPresentation.launched
|
||||
? 1.0
|
||||
: (progress ?? 0);
|
||||
final realmStatus = switch (realmOnline) {
|
||||
true => 'ONLINE',
|
||||
false => 'OFFLINE',
|
||||
null => 'STATUS UNKNOWN',
|
||||
};
|
||||
final realmMetadata = realmGameBuild != null && realmGameBuild! > 0
|
||||
? '$realmStatus · BUILD $realmGameBuild'
|
||||
: realmStatus;
|
||||
final label = switch (state) {
|
||||
MwSyncPresentation.install => 'УСТАНОВИТЬ',
|
||||
MwSyncPresentation.paused => 'ПРОДОЛЖИТЬ',
|
||||
@@ -503,26 +528,28 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
border: Border.all(color: _border),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: const Row(
|
||||
child: Row(
|
||||
children: [
|
||||
_PulseDot(),
|
||||
SizedBox(width: 12),
|
||||
_PulseDot(online: realmOnline),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"ELUNE'S GRACE",
|
||||
style: TextStyle(
|
||||
(realmName?.trim().isNotEmpty ?? false)
|
||||
? realmName!.toUpperCase()
|
||||
: 'REALM UNAVAILABLE',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Cinzel',
|
||||
color: _ivory,
|
||||
fontSize: 11,
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 3),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
'ONLINE · EU',
|
||||
style: TextStyle(
|
||||
realmMetadata,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 8,
|
||||
@@ -592,14 +619,20 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
ready ? 'Последняя проверка: только что' : '',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 8,
|
||||
if (ready)
|
||||
const Flexible(
|
||||
child: Text(
|
||||
'Последняя проверка: только что',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -647,17 +680,27 @@ class MwSyncActionArea extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _PulseDot extends StatelessWidget {
|
||||
const _PulseDot();
|
||||
const _PulseDot({required this.online});
|
||||
|
||||
final bool? online;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF6FC28A),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [BoxShadow(color: Color(0xAA6FC28A), blurRadius: 8)],
|
||||
),
|
||||
);
|
||||
Widget build(BuildContext context) {
|
||||
final color = switch (online) {
|
||||
true => const Color(0xFF6FC28A),
|
||||
false => const Color(0xFFD76A78),
|
||||
null => _muted,
|
||||
};
|
||||
return Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [BoxShadow(color: color.withAlpha(170), blurRadius: 8)],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwLauncherStatusBar extends StatelessWidget {
|
||||
@@ -709,87 +752,67 @@ class MwLauncherStatusBar extends StatelessWidget {
|
||||
}
|
||||
|
||||
class MwAccountAffordance extends StatelessWidget {
|
||||
const MwAccountAffordance({
|
||||
super.key,
|
||||
required this.onLogout,
|
||||
this.onSettings,
|
||||
});
|
||||
const MwAccountAffordance({super.key, required this.onLogout, this.username});
|
||||
final VoidCallback onLogout;
|
||||
final VoidCallback? onSettings;
|
||||
final String? username;
|
||||
@override
|
||||
Widget build(BuildContext context) => PopupMenuButton<String>(
|
||||
tooltip: 'Аккаунт',
|
||||
onSelected: (value) =>
|
||||
value == 'settings' ? onSettings?.call() : onLogout(),
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(value: 'settings', child: Text('Настройки')),
|
||||
PopupMenuItem(value: 'logout', child: Text('Выйти')),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(8, 7, 15, 7),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x99070A18),
|
||||
border: Border.all(color: _border),
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: Color(0xFF5B3A8A),
|
||||
child: Text(
|
||||
'Æ',
|
||||
style: TextStyle(fontFamily: 'Cinzel', fontSize: 12),
|
||||
Widget build(BuildContext context) {
|
||||
final displayUsername = (username?.trim().isNotEmpty ?? false)
|
||||
? username!.trim()
|
||||
: 'Аккаунт';
|
||||
final initial = displayUsername.characters.first.toUpperCase();
|
||||
|
||||
return PopupMenuButton<String>(
|
||||
tooltip: 'Аккаунт',
|
||||
onSelected: (_) => onLogout(),
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(value: 'logout', child: Text('Выйти')),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.fromLTRB(8, 7, 15, 7),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x99070A18),
|
||||
border: Border.all(color: _border),
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: const Color(0xFF5B3A8A),
|
||||
child: Text(
|
||||
initial,
|
||||
style: const TextStyle(fontFamily: 'Cinzel', fontSize: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 11),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Aranthel',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cinzel',
|
||||
color: _ivory,
|
||||
fontSize: 12,
|
||||
letterSpacing: 1,
|
||||
),
|
||||
const SizedBox(width: 11),
|
||||
Text(
|
||||
displayUsername,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Cinzel',
|
||||
color: _ivory,
|
||||
fontSize: 12,
|
||||
letterSpacing: 1,
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
'ХРАНИТЕЛЬ РОЩИ',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: _muted,
|
||||
fontSize: 7,
|
||||
letterSpacing: 1.1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwSettingsPanel extends StatelessWidget {
|
||||
const MwSettingsPanel({
|
||||
super.key,
|
||||
required this.installationPath,
|
||||
required this.themeVariant,
|
||||
required this.onChooseDirectory,
|
||||
required this.onThemeChanged,
|
||||
required this.onVerify,
|
||||
required this.onLogout,
|
||||
this.syncActive = false,
|
||||
});
|
||||
final String? installationPath;
|
||||
final MoonWellThemeVariant themeVariant;
|
||||
final VoidCallback onChooseDirectory;
|
||||
final ValueChanged<MoonWellThemeVariant> onThemeChanged;
|
||||
final VoidCallback onVerify;
|
||||
final VoidCallback onLogout;
|
||||
final bool syncActive;
|
||||
@override
|
||||
Widget build(BuildContext context) => MwPanel(
|
||||
@@ -812,32 +835,11 @@ class MwSettingsPanel extends StatelessWidget {
|
||||
variant: MwButtonVariant.secondary,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SegmentedButton<MoonWellThemeVariant>(
|
||||
segments: const [
|
||||
ButtonSegment(
|
||||
value: MoonWellThemeVariant.forest,
|
||||
label: Text('Лес'),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: MoonWellThemeVariant.temple,
|
||||
label: Text('Храм'),
|
||||
),
|
||||
],
|
||||
selected: {themeVariant},
|
||||
onSelectionChanged: (value) => onThemeChanged(value.single),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
MwButton(
|
||||
label: 'Проверить файлы',
|
||||
onPressed: syncActive ? null : onVerify,
|
||||
variant: MwButtonVariant.secondary,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
MwButton(
|
||||
label: 'Выйти',
|
||||
onPressed: onLogout,
|
||||
variant: MwButtonVariant.destructive,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user