diff --git a/lib/app.dart b/lib/app.dart index e10b723..5121260 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -49,6 +49,7 @@ abstract class AppGlobalState with _$AppGlobalState { @Default(ThemeConf()) ThemeConf themeConf, Locale? appLocale, Box? appConfBox, + @Default(10) windowsVersion, }) = _AppGlobalState; } @@ -56,17 +57,15 @@ abstract class AppGlobalState with _$AppGlobalState { GoRouter router(Ref ref) { return GoRouter( routes: [ - GoRoute( - path: '/', - pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI()), - ), + GoRoute(path: '/', pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI())), GoRoute( path: '/index', pageBuilder: (context, state) => myPageBuilder(context, state, const IndexUI()), routes: [ GoRoute( - path: "downloader", - pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI())), + path: "downloader", + pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI()), + ), GoRoute( path: 'game_doctor', pageBuilder: (context, state) => myPageBuilder(context, state, const HomeGameDoctorUI()), @@ -76,17 +75,19 @@ GoRouter router(Ref ref) { pageBuilder: (context, state) => myPageBuilder(context, state, const HomePerformanceUI()), ), GoRoute( - path: 'advanced_localization', - pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI())) + path: 'advanced_localization', + pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI()), + ), ], ), - GoRoute(path: '/tools', builder: (_, _) => const SizedBox(), routes: [ - GoRoute( - path: 'unp4kc', - pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI()), - ), - ]), - GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())) + GoRoute( + path: '/tools', + builder: (_, _) => const SizedBox(), + routes: [ + GoRoute(path: 'unp4kc', pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI())), + ], + ), + GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())), ], ); } @@ -94,13 +95,13 @@ GoRouter router(Ref ref) { @riverpod class AppGlobalModel extends _$AppGlobalModel { static Map get appLocaleSupport => { - const Locale("auto"): S.current.settings_app_language_auto, - const Locale("zh", "CN"): NoL10n.langZHS, - const Locale("zh", "TW"): NoL10n.langZHT, - const Locale("en"): NoL10n.langEn, - const Locale("ja"): NoL10n.langJa, - const Locale("ru"): NoL10n.langRU, - }; + const Locale("auto"): S.current.settings_app_language_auto, + const Locale("zh", "CN"): NoL10n.langZHS, + const Locale("zh", "TW"): NoL10n.langZHT, + const Locale("en"): NoL10n.langEn, + const Locale("ja"): NoL10n.langJa, + const Locale("ru"): NoL10n.langRU, + }; @override AppGlobalState build() { @@ -174,9 +175,9 @@ class AppGlobalModel extends _$AppGlobalModel { await Window.initialize(); await Window.hideWindowControls(); if (windowsDeviceInfo?.productName.contains("Windows 11") ?? false) { - await Window.setEffect( - effect: WindowEffect.acrylic, - ); + await Window.setEffect(effect: WindowEffect.acrylic); + state = state.copyWith(windowsVersion: 11); + dPrint("---- Windows 11 Acrylic Effect init -----"); } } }); @@ -226,18 +227,24 @@ class AppGlobalModel extends _$AppGlobalModel { if (state.networkVersionData == null) { if (!context.mounted) return false; await showToast( - context, S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString())); + context, + S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString()), + ); return false; } if (!Platform.isWindows) return false; - final lastVersion = - ConstConf.isMSE ? state.networkVersionData?.mSELastVersionCode : state.networkVersionData?.lastVersionCode; + final lastVersion = ConstConf.isMSE + ? state.networkVersionData?.mSELastVersionCode + : state.networkVersionData?.lastVersionCode; if ((lastVersion ?? 0) > ConstConf.appVersionCode) { // need update if (!context.mounted) return false; - final r = - await showDialog(dismissWithEsc: false, context: context, builder: (context) => const UpgradeDialogUI()); + final r = await showDialog( + dismissWithEsc: false, + context: context, + builder: (context) => const UpgradeDialogUI(), + ); if (r != true) { if (!context.mounted) return false; @@ -264,8 +271,10 @@ class AppGlobalModel extends _$AppGlobalModel { dPrint("now == $now start == $startTime end == $endTime"); if (now < startTime) { - _activityThemeColorTimer = - Timer(Duration(milliseconds: startTime - now), () => checkActivityThemeColor(networkVersionData)); + _activityThemeColorTimer = Timer( + Duration(milliseconds: startTime - now), + () => checkActivityThemeColor(networkVersionData), + ); dPrint("start Timer ...."); } else if (now >= startTime && now <= endTime) { dPrint("update Color ...."); @@ -280,8 +289,10 @@ class AppGlobalModel extends _$AppGlobalModel { ); // wait for end - _activityThemeColorTimer = - Timer(Duration(milliseconds: endTime - now), () => checkActivityThemeColor(networkVersionData)); + _activityThemeColorTimer = Timer( + Duration(milliseconds: endTime - now), + () => checkActivityThemeColor(networkVersionData), + ); } else { dPrint("reset Color ...."); state = state.copyWith( @@ -302,8 +313,9 @@ class AppGlobalModel extends _$AppGlobalModel { await appConfBox.put("app_locale", null); return; } - final localeCode = - value.countryCode != null ? "${value.languageCode}_${value.countryCode ?? ""}" : value.languageCode; + final localeCode = value.countryCode != null + ? "${value.languageCode}_${value.countryCode ?? ""}" + : value.languageCode; dPrint("changeLocale == $value localeCode=== $localeCode"); await appConfBox.put("app_locale", localeCode); state = state.copyWith(appLocale: value); diff --git a/lib/app.freezed.dart b/lib/app.freezed.dart index 65c7d9a..d938456 100644 --- a/lib/app.freezed.dart +++ b/lib/app.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$AppGlobalState { - String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; + String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; dynamic get windowsVersion; /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $AppGlobalStateCopyWith get copyWith => _$AppGlobalStateCopyWith @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion)); } @override -int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox); +int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion)); @override String toString() { - return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)'; + return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)'; } @@ -45,7 +45,7 @@ abstract mixin class $AppGlobalStateCopyWith<$Res> { factory $AppGlobalStateCopyWith(AppGlobalState value, $Res Function(AppGlobalState) _then) = _$AppGlobalStateCopyWithImpl; @useResult $Res call({ - String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox + String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion }); @@ -62,7 +62,7 @@ class _$AppGlobalStateCopyWithImpl<$Res> /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) { return _then(_self.copyWith( deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable @@ -71,7 +71,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable -as Box?, +as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable +as dynamic, )); } /// Create a copy of AppGlobalState @@ -165,10 +166,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _AppGlobalState() when $default != null: -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _: return orElse(); } @@ -186,10 +187,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion) $default,) {final _that = this; switch (_that) { case _AppGlobalState(): -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _: throw StateError('Unexpected subclass'); } @@ -206,10 +207,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,) {final _that = this; switch (_that) { case _AppGlobalState() when $default != null: -return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _: +return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _: return null; } @@ -221,7 +222,7 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi class _AppGlobalState implements AppGlobalState { - const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox}); + const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox, this.windowsVersion = 10}); @override final String? deviceUUID; @@ -231,6 +232,7 @@ class _AppGlobalState implements AppGlobalState { @override@JsonKey() final ThemeConf themeConf; @override final Locale? appLocale; @override final Box? appConfBox; +@override@JsonKey() final dynamic windowsVersion; /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. @@ -242,16 +244,16 @@ _$AppGlobalStateCopyWith<_AppGlobalState> get copyWith => __$AppGlobalStateCopyW @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion)); } @override -int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox); +int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion)); @override String toString() { - return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)'; + return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)'; } @@ -262,7 +264,7 @@ abstract mixin class _$AppGlobalStateCopyWith<$Res> implements $AppGlobalStateCo factory _$AppGlobalStateCopyWith(_AppGlobalState value, $Res Function(_AppGlobalState) _then) = __$AppGlobalStateCopyWithImpl; @override @useResult $Res call({ - String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox + String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion }); @@ -279,7 +281,7 @@ class __$AppGlobalStateCopyWithImpl<$Res> /// Create a copy of AppGlobalState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) { return _then(_AppGlobalState( deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable @@ -288,7 +290,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable -as Box?, +as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable +as dynamic, )); } diff --git a/lib/app.g.dart b/lib/app.g.dart index ad8194c..74b51d6 100644 --- a/lib/app.g.dart +++ b/lib/app.g.dart @@ -82,7 +82,7 @@ final class AppGlobalModelProvider } } -String _$appGlobalModelHash() => r'53dd9ed5e197333b509d282eb01073f15572820c'; +String _$appGlobalModelHash() => r'51f72c5d8538e2a4f11d256802b1a1f2e04d03be'; abstract class _$AppGlobalModel extends $Notifier { AppGlobalState build(); diff --git a/lib/common/utils/multi_window_manager.dart b/lib/common/utils/multi_window_manager.dart index 44f4e3f..9a70968 100644 --- a/lib/common/utils/multi_window_manager.dart +++ b/lib/common/utils/multi_window_manager.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_acrylic/flutter_acrylic.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,6 +13,7 @@ import 'package:starcitizen_doctor/common/conf/conf.dart'; import 'package:starcitizen_doctor/common/helper/log_helper.dart'; import 'package:starcitizen_doctor/generated/l10n.dart'; import 'package:starcitizen_doctor/ui/tools/log_analyze_ui/log_analyze_ui.dart'; +import 'package:window_manager/window_manager.dart'; import 'base_utils.dart'; @@ -18,6 +21,15 @@ part 'multi_window_manager.freezed.dart'; part 'multi_window_manager.g.dart'; +/// Window type definitions for multi-window support +class WindowTypes { + /// Main application window + static const String main = 'main'; + + /// Log analyzer window + static const String logAnalyze = 'log_analyze'; +} + @freezed abstract class MultiWindowAppState with _$MultiWindowAppState { const factory MultiWindowAppState({ @@ -27,35 +39,49 @@ abstract class MultiWindowAppState with _$MultiWindowAppState { required List gameInstallPaths, String? languageCode, String? countryCode, + @Default(10) windowsVersion, }) = _MultiWindowAppState; factory MultiWindowAppState.fromJson(Map json) => _$MultiWindowAppStateFromJson(json); } class MultiWindowManager { - static Future launchSubWindow(String type, String title, AppGlobalState appGlobalState) async { - final gameInstallPaths = await SCLoggerHelper.getGameInstallPath(await SCLoggerHelper.getLauncherLogList() ?? [], - checkExists: true, withVersion: AppConf.gameChannels); - final window = await DesktopMultiWindow.createWindow(jsonEncode({ - 'window_type': type, - 'app_state': _appStateToWindowState( - appGlobalState, - gameInstallPaths: gameInstallPaths, - ).toJson(), - })); - window.setFrame(const Rect.fromLTWH(0, 0, 900, 1200)); - window.setTitle(title); - await window.center(); - await window.show(); - // sendAppStateBroadcast(appGlobalState); + /// Parse window type from arguments string + static String parseWindowType(String arguments) { + if (arguments.isEmpty) { + return WindowTypes.main; + } + try { + final Map argument = jsonDecode(arguments); + return argument['window_type'] ?? WindowTypes.main; + } catch (e) { + return WindowTypes.main; + } } - static void sendAppStateBroadcast(AppGlobalState appGlobalState) { - DesktopMultiWindow.invokeMethod( - 0, - 'app_state_broadcast', - _appStateToWindowState(appGlobalState).toJson(), + /// Launch a sub-window with specified type and title + static Future launchSubWindow(String type, String title, AppGlobalState appGlobalState) async { + final gameInstallPaths = await SCLoggerHelper.getGameInstallPath( + await SCLoggerHelper.getLauncherLogList() ?? [], + checkExists: true, + withVersion: AppConf.gameChannels, ); + + final controller = await WindowController.create( + WindowConfiguration( + hiddenAtLaunch: true, + arguments: jsonEncode({ + 'window_type': type, + 'app_state': _appStateToWindowState(appGlobalState, gameInstallPaths: gameInstallPaths).toJson(), + }), + ), + ); + await Future.delayed(Duration(milliseconds: 500)).then((_) async { + await controller.setFrame(const Rect.fromLTWH(0, 0, 800, 1200)); + await controller.setTitle(title); + await controller.center(); + await controller.show(); + }); } static MultiWindowAppState _appStateToWindowState(AppGlobalState appGlobalState, {List? gameInstallPaths}) { @@ -66,53 +92,147 @@ class MultiWindowManager { languageCode: appGlobalState.appLocale?.languageCode, countryCode: appGlobalState.appLocale?.countryCode, gameInstallPaths: gameInstallPaths ?? [], + windowsVersion: appGlobalState.windowsVersion, ); } - static void runSubWindowApp(List args) { - final argument = args[2].isEmpty ? const {} : jsonDecode(args[2]) as Map; + /// Run sub-window app with parsed arguments + static Future runSubWindowApp(String arguments, String windowType) async { + final Map argument = arguments.isEmpty ? const {} : jsonDecode(arguments); final windowAppState = MultiWindowAppState.fromJson(argument['app_state'] ?? {}); Widget? windowWidget; - switch (argument["window_type"]) { - case "log_analyze": + + switch (windowType) { + case WindowTypes.logAnalyze: windowWidget = ToolsLogAnalyzeDialogUI(appState: windowAppState); break; default: - throw Exception('Unknown window type'); + throw Exception('Unknown window type: $windowType'); } - return runApp(ProviderScope( - child: FluentApp( - title: "StarCitizenToolBox", - restorationScopeId: "StarCitizenToolBox", - themeMode: ThemeMode.dark, - localizationsDelegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - FluentLocalizations.delegate, - S.delegate, - ], - supportedLocales: S.delegate.supportedLocales, - home: windowWidget, - theme: FluentThemeData( + + await Window.initialize(); + + if (windowAppState.windowsVersion >= 10) { + await Window.setEffect(effect: WindowEffect.acrylic); + } + + final backgroundColor = HexColor(windowAppState.backgroundColor).withValues(alpha: .1); + + return runApp( + ProviderScope( + child: FluentApp( + title: "StarCitizenToolBox", + restorationScopeId: "StarCitizenToolBox", + themeMode: ThemeMode.dark, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + FluentLocalizations.delegate, + S.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + home: windowWidget, + theme: FluentThemeData( brightness: Brightness.dark, fontFamily: "SourceHanSansCN-Regular", - navigationPaneTheme: NavigationPaneThemeData( - backgroundColor: HexColor(windowAppState.backgroundColor), - ), + navigationPaneTheme: NavigationPaneThemeData(backgroundColor: backgroundColor), menuColor: HexColor(windowAppState.menuColor), micaBackgroundColor: HexColor(windowAppState.micaColor), + scaffoldBackgroundColor: backgroundColor, buttonTheme: ButtonThemeData( - defaultButtonStyle: ButtonStyle( - shape: WidgetStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - side: BorderSide(color: Colors.white.withValues(alpha: .01)))), - ))), - locale: windowAppState.languageCode != null - ? Locale(windowAppState.languageCode!, windowAppState.countryCode) - : null, - debugShowCheckedModeBanner: false, + defaultButtonStyle: ButtonStyle( + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + side: BorderSide(color: Colors.white.withValues(alpha: .01)), + ), + ), + ), + ), + ), + locale: windowAppState.languageCode != null + ? Locale(windowAppState.languageCode!, windowAppState.countryCode) + : null, + debugShowCheckedModeBanner: false, + ), ), - )); + ); + } +} + +/// Extension methods for WindowController to add custom functionality +extension WindowControllerExtension on WindowController { + /// Initialize custom window method handlers + Future doCustomInitialize() async { + windowManager.ensureInitialized(); + return await setWindowMethodHandler((call) async { + switch (call.method) { + case 'window_center': + return await windowManager.center(); + case 'window_close': + return await windowManager.close(); + case 'window_show': + return await windowManager.show(); + case 'window_hide': + return await windowManager.hide(); + case 'window_focus': + return await windowManager.focus(); + case 'window_set_frame': + final args = call.arguments as Map; + return await windowManager.setBounds( + Rect.fromLTWH( + args['left'] as double, + args['top'] as double, + args['width'] as double, + args['height'] as double, + ), + ); + case 'window_set_title': + return await windowManager.setTitle(call.arguments as String); + default: + throw MissingPluginException('Not implemented: ${call.method}'); + } + }); + } + + /// Center the window + Future center() { + return invokeMethod('window_center'); + } + + /// Close the window + void close() async { + await invokeMethod('window_close'); + } + + /// Show the window + Future show() { + return invokeMethod('window_show'); + } + + /// Hide the window + Future hide() { + return invokeMethod('window_hide'); + } + + /// Focus the window + Future focus() { + return invokeMethod('window_focus'); + } + + /// Set window frame (position and size) + Future setFrame(Rect frame) { + return invokeMethod('window_set_frame', { + 'left': frame.left, + 'top': frame.top, + 'width': frame.width, + 'height': frame.height, + }); + } + + /// Set window title + Future setTitle(String title) { + return invokeMethod('window_set_title', title); } } diff --git a/lib/common/utils/multi_window_manager.freezed.dart b/lib/common/utils/multi_window_manager.freezed.dart index 3c0fdfc..ca40755 100644 --- a/lib/common/utils/multi_window_manager.freezed.dart +++ b/lib/common/utils/multi_window_manager.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$MultiWindowAppState { - String get backgroundColor; String get menuColor; String get micaColor; List get gameInstallPaths; String? get languageCode; String? get countryCode; + String get backgroundColor; String get menuColor; String get micaColor; List get gameInstallPaths; String? get languageCode; String? get countryCode; dynamic get windowsVersion; /// Create a copy of MultiWindowAppState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $MultiWindowAppStateCopyWith get copyWith => _$MultiWindowA @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other.gameInstallPaths, gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other.gameInstallPaths, gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(gameInstallPaths),languageCode,countryCode); +int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion)); @override String toString() { - return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode)'; + return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)'; } @@ -48,7 +48,7 @@ abstract mixin class $MultiWindowAppStateCopyWith<$Res> { factory $MultiWindowAppStateCopyWith(MultiWindowAppState value, $Res Function(MultiWindowAppState) _then) = _$MultiWindowAppStateCopyWithImpl; @useResult $Res call({ - String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode + String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion }); @@ -65,7 +65,7 @@ class _$MultiWindowAppStateCopyWithImpl<$Res> /// Create a copy of MultiWindowAppState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) { return _then(_self.copyWith( backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable @@ -73,7 +73,8 @@ as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: as String,gameInstallPaths: null == gameInstallPaths ? _self.gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable as List,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable -as String?, +as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable +as dynamic, )); } @@ -158,10 +159,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _MultiWindowAppState() when $default != null: -return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _: +return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _: return orElse(); } @@ -179,10 +180,10 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion) $default,) {final _that = this; switch (_that) { case _MultiWindowAppState(): -return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _: +return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _: throw StateError('Unexpected subclass'); } @@ -199,10 +200,10 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,) {final _that = this; switch (_that) { case _MultiWindowAppState() when $default != null: -return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _: +return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _: return null; } @@ -214,7 +215,7 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game @JsonSerializable() class _MultiWindowAppState implements MultiWindowAppState { - const _MultiWindowAppState({required this.backgroundColor, required this.menuColor, required this.micaColor, required final List gameInstallPaths, this.languageCode, this.countryCode}): _gameInstallPaths = gameInstallPaths; + const _MultiWindowAppState({required this.backgroundColor, required this.menuColor, required this.micaColor, required final List gameInstallPaths, this.languageCode, this.countryCode, this.windowsVersion = 10}): _gameInstallPaths = gameInstallPaths; factory _MultiWindowAppState.fromJson(Map json) => _$MultiWindowAppStateFromJson(json); @override final String backgroundColor; @@ -229,6 +230,7 @@ class _MultiWindowAppState implements MultiWindowAppState { @override final String? languageCode; @override final String? countryCode; +@override@JsonKey() final dynamic windowsVersion; /// Create a copy of MultiWindowAppState /// with the given fields replaced by the non-null parameter values. @@ -243,16 +245,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other._gameInstallPaths, _gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other._gameInstallPaths, _gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(_gameInstallPaths),languageCode,countryCode); +int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(_gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion)); @override String toString() { - return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode)'; + return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)'; } @@ -263,7 +265,7 @@ abstract mixin class _$MultiWindowAppStateCopyWith<$Res> implements $MultiWindow factory _$MultiWindowAppStateCopyWith(_MultiWindowAppState value, $Res Function(_MultiWindowAppState) _then) = __$MultiWindowAppStateCopyWithImpl; @override @useResult $Res call({ - String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode + String backgroundColor, String menuColor, String micaColor, List gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion }); @@ -280,7 +282,7 @@ class __$MultiWindowAppStateCopyWithImpl<$Res> /// Create a copy of MultiWindowAppState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) { return _then(_MultiWindowAppState( backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable @@ -288,7 +290,8 @@ as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore: as String,gameInstallPaths: null == gameInstallPaths ? _self._gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable as List,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable -as String?, +as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable +as dynamic, )); } diff --git a/lib/common/utils/multi_window_manager.g.dart b/lib/common/utils/multi_window_manager.g.dart index 829da24..a0be5c9 100644 --- a/lib/common/utils/multi_window_manager.g.dart +++ b/lib/common/utils/multi_window_manager.g.dart @@ -16,6 +16,7 @@ _MultiWindowAppState _$MultiWindowAppStateFromJson(Map json) => .toList(), languageCode: json['languageCode'] as String?, countryCode: json['countryCode'] as String?, + windowsVersion: json['windowsVersion'] ?? 10, ); Map _$MultiWindowAppStateToJson( @@ -27,4 +28,5 @@ Map _$MultiWindowAppStateToJson( 'gameInstallPaths': instance.gameInstallPaths, 'languageCode': instance.languageCode, 'countryCode': instance.countryCode, + 'windowsVersion': instance.windowsVersion, }; diff --git a/lib/main.dart b/lib/main.dart index 956782c..dfe5b1d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,24 +11,41 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'app.dart'; import 'common/utils/multi_window_manager.dart'; -void main(List args) async { +Future main(List args) async { // webview window - if (runWebViewTitleBarWidget(args, - backgroundColor: const Color.fromRGBO(19, 36, 49, 1), builder: _defaultWebviewTitleBar)) { - return; - } - if (args.firstOrNull == 'multi_window') { - MultiWindowManager.runSubWindowApp(args); + if (runWebViewTitleBarWidget( + args, + backgroundColor: const Color.fromRGBO(19, 36, 49, 1), + builder: _defaultWebviewTitleBar, + )) { return; } + WidgetsFlutterBinding.ensureInitialized(); - await _initWindow(); - // run app - runApp(const ProviderScope(child: App())); + await windowManager.ensureInitialized(); + + // Get the current window controller + final windowController = await WindowController.fromCurrentEngine(); + + // Parse window arguments to determine which window to show + final windowType = MultiWindowManager.parseWindowType(windowController.arguments); + + // Initialize window-specific handlers for sub-windows + if (windowType != WindowTypes.main) { + await windowController.doCustomInitialize(); + } + + // Run different apps based on the window type + switch (windowType) { + case WindowTypes.main: + await _initWindow(); + runApp(const ProviderScope(child: App())); + default: + MultiWindowManager.runSubWindowApp(windowController.arguments, windowType); + } } Future _initWindow() async { - await windowManager.ensureInitialized(); await windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: false); await windowManager.setSize(const Size(1280, 810)); await windowManager.setMinimumSize(const Size(1280, 810)); @@ -97,7 +114,22 @@ class App extends HookConsumerWidget with WindowListener { @override Future onWindowClose() async { debugPrint("onWindowClose"); - exit(0); + if (await windowManager.isPreventClose()) { + final mainWindow = await WindowController.fromCurrentEngine(); + final windows = await WindowController.getAll(); + for (final controller in windows) { + if (controller.windowId != mainWindow.windowId) { + try { + controller.close(); + } catch (e) { + debugPrint("Error closing window ${controller.windowId}: $e"); + } + } + } + await windowManager.close(); + await windowManager.destroy(); + exit(0); + } super.onWindowClose(); } } diff --git a/lib/provider/aria2c.g.dart b/lib/provider/aria2c.g.dart index ae3f316..8e2b783 100644 --- a/lib/provider/aria2c.g.dart +++ b/lib/provider/aria2c.g.dart @@ -41,7 +41,7 @@ final class Aria2cModelProvider } } -String _$aria2cModelHash() => r'eb45d6aa9fc641abceb34ad5685aab57aa7a870b'; +String _$aria2cModelHash() => r'17956c60a79c68ae13b8b8e700ebbafb70e93194'; abstract class _$Aria2cModel extends $Notifier { Aria2cModelState build(); diff --git a/lib/ui/home/input_method/input_method_dialog_ui_model.g.dart b/lib/ui/home/input_method/input_method_dialog_ui_model.g.dart index 5e49b7e..6270440 100644 --- a/lib/ui/home/input_method/input_method_dialog_ui_model.g.dart +++ b/lib/ui/home/input_method/input_method_dialog_ui_model.g.dart @@ -43,7 +43,7 @@ final class InputMethodDialogUIModelProvider } String _$inputMethodDialogUIModelHash() => - r'39b7fc1446c09514b837c0f181488d34a4391751'; + r'f216c1a5b6d68b3924af7b351314c618dcac80b5'; abstract class _$InputMethodDialogUIModel extends $Notifier { @@ -115,7 +115,7 @@ final class OnnxTranslationProvider } } -String _$onnxTranslationHash() => r'05b7b063a1013eed1ee4daae5212b3b6c555cd82'; +String _$onnxTranslationHash() => r'4f3dc0e361dca2d6b00f557496bdf006cc6c235c'; final class OnnxTranslationFamily extends $Family with diff --git a/lib/ui/tools/tools_ui_model.dart b/lib/ui/tools/tools_ui_model.dart index bbe837e..b049272 100644 --- a/lib/ui/tools/tools_ui_model.dart +++ b/lib/ui/tools/tools_ui_model.dart @@ -175,7 +175,8 @@ class ToolsUIModel extends _$ToolsUIModel { "remove_nvme_settings", S.current.tools_action_remove_nvme_registry_patch, S.current.tools_action_info_nvme_patch_issue( - nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed), + nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed, + ), const Icon(FluentIcons.hard_drive, size: 24), onTap: nvmePatchStatus ? () async { @@ -207,7 +208,7 @@ class ToolsUIModel extends _$ToolsUIModel { state = state.copyWith(working: false); loadToolsCard(context, skipPathScan: true); }, - ) + ), ]; } @@ -265,8 +266,11 @@ class ToolsUIModel extends _$ToolsUIModel { if (listData == null) { return; } - scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData, - checkExists: checkActive, withVersion: AppConf.gameChannels); + scInstallPaths = await SCLoggerHelper.getGameInstallPath( + listData, + checkExists: checkActive, + withVersion: AppConf.gameChannels, + ); if (scInstallPaths.isNotEmpty) { scInstalledPath = scInstallPaths.first; } @@ -336,11 +340,12 @@ class ToolsUIModel extends _$ToolsUIModel { Future getSystemInfo() async { return S.current.tools_action_info_system_info_content( - await SystemHelper.getSystemName(), - await SystemHelper.getCpuName(), - await SystemHelper.getSystemMemorySizeGB(), - await SystemHelper.getGpuInfo(), - await SystemHelper.getDiskInfo()); + await SystemHelper.getSystemName(), + await SystemHelper.getCpuName(), + await SystemHelper.getSystemMemorySizeGB(), + await SystemHelper.getGpuInfo(), + await SystemHelper.getDiskInfo(), + ); } /// 管理员模式运行 RSI 启动器 @@ -364,9 +369,7 @@ class ToolsUIModel extends _$ToolsUIModel { builder: (context) => ContentDialog( title: Text(S.current.tools_action_info_system_info_title), content: Text(systemInfo), - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * .65, - ), + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .65), actions: [ FilledButton( child: Padding( @@ -403,8 +406,11 @@ class ToolsUIModel extends _$ToolsUIModel { if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) { if (!context.mounted) return; - showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + showToast( + context, + S.current.tools_action_info_rsi_launcher_running_warning, + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); return; } @@ -436,8 +442,11 @@ class ToolsUIModel extends _$ToolsUIModel { return; } - final userSelect = - await FilePicker.platform.saveFile(initialDirectory: savePath, fileName: fileName, lockParentWindow: true); + final userSelect = await FilePicker.platform.saveFile( + initialDirectory: savePath, + fileName: fileName, + lockParentWindow: true, + ); if (userSelect == null) { state = state.copyWith(working: false); return; @@ -546,16 +555,18 @@ class ToolsUIModel extends _$ToolsUIModel { static Future rsiEnhance(BuildContext context, {bool showNotGameInstallMsg = false}) async { if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) { if (!context.mounted) return; - showToast(context, S.current.tools_action_info_rsi_launcher_running_warning, - constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35)); + showToast( + context, + S.current.tools_action_info_rsi_launcher_running_warning, + constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35), + ); return; } if (!context.mounted) return; showDialog( - context: context, - builder: (BuildContext context) => RsiLauncherEnhanceDialogUI( - showNotGameInstallMsg: showNotGameInstallMsg, - )); + context: context, + builder: (BuildContext context) => RsiLauncherEnhanceDialogUI(showNotGameInstallMsg: showNotGameInstallMsg), + ); } Future _showLogAnalyze(BuildContext context) async { @@ -564,6 +575,10 @@ class ToolsUIModel extends _$ToolsUIModel { return; } if (!context.mounted) return; - await MultiWindowManager.launchSubWindow("log_analyze", S.current.log_analyzer_window_title, appGlobalState); + await MultiWindowManager.launchSubWindow( + WindowTypes.logAnalyze, + S.current.log_analyzer_window_title, + appGlobalState, + ); } } diff --git a/lib/ui/tools/tools_ui_model.g.dart b/lib/ui/tools/tools_ui_model.g.dart index 367e182..23d0d07 100644 --- a/lib/ui/tools/tools_ui_model.g.dart +++ b/lib/ui/tools/tools_ui_model.g.dart @@ -41,7 +41,7 @@ final class ToolsUIModelProvider } } -String _$toolsUIModelHash() => r'885596b0df27191f2c69c571b0a1f60d9c6e31de'; +String _$toolsUIModelHash() => r'78732ff16e87cc9f92174bda43d0fafadba51146'; abstract class _$ToolsUIModel extends $Notifier { ToolsUIState build(); diff --git a/linux/my_application.cc b/linux/my_application.cc index c4b824f..0b13577 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -7,6 +7,8 @@ #include "flutter/generated_plugin_registrant.h" +#include "desktop_multi_window/desktop_multi_window_plugin.h" + struct _MyApplication { GtkApplication parent_instance; char** dart_entrypoint_arguments; @@ -59,6 +61,10 @@ static void my_application_activate(GApplication* application) { fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + desktop_multi_window_plugin_set_window_created_callback([](FlPluginRegistry* registry){ + fl_register_plugins(registry); + }); + gtk_widget_grab_focus(GTK_WIDGET(view)); } diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 3cc05eb..d396133 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -1,5 +1,6 @@ import Cocoa import FlutterMacOS +import desktop_multi_window class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -9,6 +10,11 @@ class MainFlutterWindow: NSWindow { self.setFrame(windowFrame, display: true) RegisterGeneratedPlugins(registry: flutterViewController) + + FlutterMultiWindowPlugin.setOnWindowCreatedCallback { controller in + // Register the plugin which you want access from other isolate. + RegisterGeneratedPlugins(registry: controller) + } super.awakeFromNib() } diff --git a/pubspec.lock b/pubspec.lock index d8e8ad2..b652b5f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -326,10 +326,10 @@ packages: dependency: "direct main" description: name: desktop_multi_window - sha256: "3ea2d696e50c3df696aabfddbd98c220ab4dde38f12c2ab12d1103bfe00ae79b" + sha256: "60ba38725b8887b60e44d15afdcf0c3813568b5da2ccaf1e7f6fd09a380a6e24" url: "https://pub.dev" source: hosted - version: "0.2.1" + version: "0.3.0" desktop_webview_window: dependency: "direct main" description: @@ -1580,10 +1580,11 @@ packages: window_manager: dependency: "direct main" description: - name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" - url: "https://pub.dev" - source: hosted + path: "packages/window_manager" + ref: "6fae92d21b4c80ce1b8f71c1190d7970cf722bd4" + resolved-ref: "6fae92d21b4c80ce1b8f71c1190d7970cf722bd4" + url: "https://github.com/boyan01/window_manager.git" + source: git version: "0.5.1" xdg_directories: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index fdc03f6..e7fc839 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,11 +15,15 @@ dependencies: sdk: flutter flutter_riverpod: ^3.0.3 riverpod_annotation: ^3.0.3 - flutter_hooks: ^0.21.3+1 + flutter_hooks: ^0.21.3 hooks_riverpod: ^3.0.3 json_annotation: ^4.9.0 go_router: ^17.0.0 - window_manager: ^0.5.1 + window_manager: + git: + url: https://github.com/boyan01/window_manager.git + path: packages/window_manager + ref: 6fae92d21b4c80ce1b8f71c1190d7970cf722bd4 fluent_ui: 4.11.3 flutter_staggered_grid_view: ^0.7.0 flutter_acrylic: ^1.1.4 @@ -62,7 +66,7 @@ dependencies: re_highlight: ^0.0.3 shelf: ^1.4.2 qr_flutter: ^4.1.0 - desktop_multi_window: ^0.2.1 + desktop_multi_window: ^0.3.0 watcher: ^1.1.4 path: ^1.9.1 crypto: ^3.0.7 diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index 955ee30..9c69cdb 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -3,6 +3,7 @@ #include #include "flutter/generated_plugin_registrant.h" +#include "desktop_multi_window/desktop_multi_window_plugin.h" FlutterWindow::FlutterWindow(const flutter::DartProject& project) : project_(project) {} @@ -25,6 +26,12 @@ bool FlutterWindow::OnCreate() { return false; } RegisterPlugins(flutter_controller_->engine()); + DesktopMultiWindowSetWindowCreatedCallback([](void *controller) { + auto *flutter_view_controller = + reinterpret_cast(controller); + auto *registry = flutter_view_controller->engine(); + RegisterPlugins(registry); + }); SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() {