346 lines
14 KiB
Dart
346 lines
14 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:macos_ui/macos_ui.dart';
|
|
import '../rust/api/packages.dart';
|
|
import '../rust/frb_generated.dart';
|
|
import '../services/app_badge_controller.dart';
|
|
import '../services/app_settings_service.dart';
|
|
import 'theme/window_theme_style.dart';
|
|
import 'screens/cleaner_screen.dart';
|
|
import 'screens/discover_screen.dart';
|
|
import 'screens/doctor_screen.dart';
|
|
import 'screens/history_screen.dart';
|
|
import 'screens/installed_screen.dart';
|
|
import 'screens/settings_screen.dart';
|
|
import 'screens/starter_packs_screen.dart';
|
|
import 'screens/updates_screen.dart';
|
|
import 'widgets/keyboard_shortcuts_dialog.dart';
|
|
import 'widgets/package_inspector.dart';
|
|
import 'widgets/setup_wizard_dialog.dart';
|
|
import 'widgets/spotlight_search_dialog.dart';
|
|
|
|
class MainLayout extends StatefulWidget {
|
|
const MainLayout({super.key});
|
|
|
|
@override
|
|
State<MainLayout> createState() => _MainLayoutState();
|
|
}
|
|
|
|
class _MainLayoutState extends State<MainLayout> {
|
|
int _pageIndex = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_performInitialBadgeScan();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (!AppSettingsService.instance.hasCompletedSetupWizard && mounted) {
|
|
SetupWizardDialog.show(context);
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> _performInitialBadgeScan() async {
|
|
try {
|
|
final updates = await RustBridgeApi.checkUpdates();
|
|
AppBadgeController.instance.setUpdatesCount(updates.length);
|
|
} catch (_) {}
|
|
|
|
try {
|
|
final scan = await RustBridgeApi.scanCleaner();
|
|
AppBadgeController.instance.setCleanerBytes(scan.totalReclaimableBytes);
|
|
} catch (_) {}
|
|
}
|
|
|
|
void _onInspectPackage(String name, BackendType backend) {
|
|
PackageInspectorSheet.show(
|
|
context,
|
|
packageName: name,
|
|
backend: backend,
|
|
);
|
|
}
|
|
|
|
void _openSpotlight() {
|
|
SpotlightSearchDialog.show(
|
|
context,
|
|
onSelectPackage: (name, backend) {
|
|
_onInspectPackage(name, backend);
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final macosTheme = MacosTheme.of(context);
|
|
return CallbackShortcuts(
|
|
bindings: {
|
|
const SingleActivator(LogicalKeyboardKey.keyK, control: true): _openSpotlight,
|
|
const SingleActivator(LogicalKeyboardKey.keyK, meta: true): _openSpotlight,
|
|
const SingleActivator(LogicalKeyboardKey.slash, control: true): () => KeyboardShortcutsDialog.show(context),
|
|
const SingleActivator(LogicalKeyboardKey.f1): () => KeyboardShortcutsDialog.show(context),
|
|
const SingleActivator(LogicalKeyboardKey.keyR, control: true): _performInitialBadgeScan,
|
|
const SingleActivator(LogicalKeyboardKey.f5): _performInitialBadgeScan,
|
|
const SingleActivator(LogicalKeyboardKey.digit1, control: true): () => setState(() => _pageIndex = 0),
|
|
const SingleActivator(LogicalKeyboardKey.digit2, control: true): () => setState(() => _pageIndex = 1),
|
|
const SingleActivator(LogicalKeyboardKey.digit3, control: true): () => setState(() => _pageIndex = 2),
|
|
const SingleActivator(LogicalKeyboardKey.digit4, control: true): () => setState(() => _pageIndex = 3),
|
|
const SingleActivator(LogicalKeyboardKey.digit5, control: true): () => setState(() => _pageIndex = 4),
|
|
const SingleActivator(LogicalKeyboardKey.digit6, control: true): () => setState(() => _pageIndex = 5),
|
|
const SingleActivator(LogicalKeyboardKey.digit7, control: true): () => setState(() => _pageIndex = 6),
|
|
},
|
|
child: Focus(
|
|
autofocus: true,
|
|
child: MacosWindow(
|
|
sidebar: Sidebar(
|
|
topOffset: 0.0, // Убираем пустой отступ macOS traffic lights на Linux
|
|
minWidth: 200,
|
|
startWidth: 220,
|
|
maxWidth: 260,
|
|
top: Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 14, 16, 10),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
width: 30,
|
|
height: 30,
|
|
decoration: BoxDecoration(
|
|
color: macosTheme.primaryColor,
|
|
borderRadius: BorderRadius.circular(
|
|
AppSettingsService.instance.effectiveWindowThemeStyle == WindowThemeStyle.kde ? 4 : 8,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: macosTheme.primaryColor.withValues(alpha: 0.35),
|
|
blurRadius: 6,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Center(
|
|
child: MacosIcon(
|
|
AppSettingsService.instance.effectiveWindowThemeStyle == WindowThemeStyle.kde
|
|
? CupertinoIcons.gear_alt_fill
|
|
: AppSettingsService.instance.effectiveWindowThemeStyle == WindowThemeStyle.gnome
|
|
? CupertinoIcons.app_badge_fill
|
|
: CupertinoIcons.cube_box_fill,
|
|
color: CupertinoColors.white,
|
|
size: 17,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text(
|
|
'Forge',
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: -0.2,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
Text(
|
|
AppSettingsService.instance.effectiveWindowThemeStyle == WindowThemeStyle.kde
|
|
? 'KDE Breeze Edition'
|
|
: AppSettingsService.instance.effectiveWindowThemeStyle == WindowThemeStyle.gnome
|
|
? 'GNOME Adwaita'
|
|
: 'macOS Cupertino',
|
|
style: const TextStyle(
|
|
fontSize: 10.5,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
MacosTooltip(
|
|
message: 'Горячие клавиши (F1 / Ctrl+/)',
|
|
child: MacosIconButton(
|
|
icon: const MacosIcon(CupertinoIcons.keyboard, size: 14),
|
|
onPressed: () => KeyboardShortcutsDialog.show(context),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// Spotlight Search Trigger Button
|
|
CupertinoButton(
|
|
padding: EdgeInsets.zero,
|
|
minimumSize: Size.zero,
|
|
onPressed: _openSpotlight,
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6.5),
|
|
decoration: BoxDecoration(
|
|
color: CupertinoColors.white.withValues(alpha: 0.05),
|
|
borderRadius: BorderRadius.circular(7),
|
|
border: Border.all(
|
|
color: CupertinoColors.white.withValues(alpha: 0.07),
|
|
),
|
|
),
|
|
child: const Row(
|
|
children: [
|
|
MacosIcon(
|
|
CupertinoIcons.search,
|
|
size: 13,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
'Поиск пакетов...',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
'⌘K',
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.bold,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
builder: (context, scrollController) {
|
|
return ListenableBuilder(
|
|
listenable: AppBadgeController.instance,
|
|
builder: (context, _) {
|
|
final badge = AppBadgeController.instance;
|
|
|
|
return SidebarItems(
|
|
currentIndex: _pageIndex,
|
|
onChanged: (index) {
|
|
setState(() {
|
|
_pageIndex = index;
|
|
});
|
|
},
|
|
scrollController: scrollController,
|
|
itemSize: SidebarItemSize.large,
|
|
items: [
|
|
const SidebarItem(
|
|
label: Text('УПРАВЛЕНИЕ'),
|
|
section: true,
|
|
),
|
|
SidebarItem(
|
|
leading: const MacosIcon(CupertinoIcons.arrow_down_circle_fill),
|
|
label: const Text('Обновления'),
|
|
trailing: badge.updatesCount > 0
|
|
? Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 1.5),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF0A84FF),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Text(
|
|
'${badge.updatesCount}',
|
|
style: const TextStyle(
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.bold,
|
|
color: CupertinoColors.white,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.square_stack_3d_up_fill),
|
|
label: Text('Установленные'),
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.compass_fill),
|
|
label: Text('Каталог'),
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.square_grid_2x2_fill),
|
|
label: Text('Наборы ПО'),
|
|
),
|
|
const SidebarItem(
|
|
label: Text('СИСТЕМА И ОПТИМИЗАЦИЯ'),
|
|
section: true,
|
|
),
|
|
SidebarItem(
|
|
leading: const MacosIcon(CupertinoIcons.sparkles),
|
|
label: const Text('Очистка'),
|
|
trailing: badge.cleanerBytes > 0
|
|
? Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1.5),
|
|
decoration: BoxDecoration(
|
|
color: CupertinoColors.systemOrange.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Text(
|
|
AppBadgeController.formatBytes(badge.cleanerBytes),
|
|
style: const TextStyle(
|
|
fontSize: 9.5,
|
|
fontWeight: FontWeight.bold,
|
|
color: CupertinoColors.systemOrange,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.bandage_fill),
|
|
label: Text('Диагностика'),
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.clock_fill),
|
|
label: Text('История'),
|
|
),
|
|
const SidebarItem(
|
|
leading: MacosIcon(CupertinoIcons.gear_alt_fill),
|
|
label: Text('Настройки'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
bottom: MacosListTile(
|
|
leading: const MacosIcon(
|
|
CupertinoIcons.command,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
title: const Text(
|
|
'Arch / Flatpak / AUR',
|
|
style: TextStyle(fontSize: 12, color: CupertinoColors.systemGrey),
|
|
),
|
|
),
|
|
),
|
|
child: IndexedStack(
|
|
index: _pageIndex.clamp(0, 7),
|
|
children: [
|
|
UpdatesScreen(onInspectPackage: _onInspectPackage),
|
|
InstalledScreen(onInspectPackage: _onInspectPackage),
|
|
DiscoverScreen(onInspectPackage: _onInspectPackage),
|
|
const StarterPacksScreen(),
|
|
const CleanerScreen(),
|
|
const DoctorScreen(),
|
|
const HistoryScreen(),
|
|
const SettingsScreen(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|