230 lines
7.0 KiB
Dart
Executable File
230 lines
7.0 KiB
Dart
Executable File
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
|
|
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
|
import 'package:kaer_with_panels/singbox/model/singbox_status.dart';
|
|
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
|
|
|
import 'package:window_manager/window_manager.dart';
|
|
import 'package:tray_manager/tray_manager.dart';
|
|
import 'dart:io' show Platform;
|
|
|
|
import '../services/singbox_imp/kr_sing_box_imp.dart';
|
|
|
|
class KRWindowManager with WindowListener, TrayListener {
|
|
static final KRWindowManager _instance = KRWindowManager._internal();
|
|
factory KRWindowManager() => _instance;
|
|
KRWindowManager._internal();
|
|
|
|
/// 初始化窗口管理器
|
|
Future<void> kr_initWindowManager() async {
|
|
KRLogUtil.kr_i('kr_initWindowManager: 开始初始化窗口管理器');
|
|
|
|
await windowManager.ensureInitialized();
|
|
KRLogUtil.kr_i('kr_initWindowManager: 窗口管理器已初始化');
|
|
|
|
const WindowOptions windowOptions = WindowOptions(
|
|
size: Size(420, 800),
|
|
minimumSize: Size(420, 800),
|
|
maximumSize: Size(420, 800),
|
|
center: true,
|
|
backgroundColor: Colors.white,
|
|
skipTaskbar: false,
|
|
title: 'Hi快VPN',
|
|
titleBarStyle: TitleBarStyle.normal,
|
|
windowButtonVisibility: true,
|
|
);
|
|
|
|
await windowManager.waitUntilReadyToShow(windowOptions);
|
|
KRLogUtil.kr_i('kr_initWindowManager: 窗口准备就绪');
|
|
|
|
// 先添加监听器
|
|
windowManager.addListener(this);
|
|
KRLogUtil.kr_i('kr_initWindowManager: 已添加窗口监听器');
|
|
|
|
// 确保在 Windows 下正确设置窗口属性
|
|
if (Platform.isWindows) {
|
|
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
|
|
await windowManager.setTitle('HiFastVPN');
|
|
await windowManager.setSize(const Size(420, 800));
|
|
await windowManager.setMinimumSize(const Size(420, 800));
|
|
await windowManager.setMaximumSize(const Size(420, 800));
|
|
await windowManager.setResizable(false);
|
|
await windowManager.center();
|
|
await windowManager.show();
|
|
// 阻止窗口关闭
|
|
await windowManager.setPreventClose(true);
|
|
} else {
|
|
await windowManager.setTitle('HiFastVPN');
|
|
await windowManager.setSize(const Size(420, 800));
|
|
await windowManager.setMinimumSize(const Size(420, 800));
|
|
await windowManager.setMaximumSize(const Size(420, 800));
|
|
await windowManager.setResizable(false);
|
|
await windowManager.center();
|
|
}
|
|
|
|
// 初始化托盘
|
|
await _initTray();
|
|
|
|
// 初始化平台通道
|
|
_initPlatformChannel();
|
|
|
|
KRLogUtil.kr_i('kr_initWindowManager: 初始化完成');
|
|
|
|
ever(KRLanguageUtils.kr_language, (_) {
|
|
final Menu menu = Menu(
|
|
items: [
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.openDashboard,
|
|
onClick: (_) => _showWindow(),
|
|
),
|
|
if (Platform.isMacOS) MenuItem.separator(),
|
|
if (Platform.isMacOS)
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.copyToTerminal,
|
|
onClick: (_) => _copyToTerminal(),
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.exitApp,
|
|
onClick: (_) => _exitApp(),
|
|
),
|
|
],
|
|
);
|
|
trayManager.setContextMenu(menu);
|
|
});
|
|
}
|
|
|
|
/// 初始化平台通道
|
|
void _initPlatformChannel() {
|
|
if (Platform.isMacOS) {
|
|
const platform = MethodChannel('hifast_vpn/terminate');
|
|
platform.setMethodCallHandler((call) async {
|
|
if (call.method == 'onTerminate') {
|
|
KRLogUtil.kr_i('收到应用终止通知');
|
|
await _handleTerminate();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// 初始化托盘
|
|
Future<void> _initTray() async {
|
|
KRLogUtil.kr_i('_initTray: 开始初始化托盘');
|
|
trayManager.addListener(this);
|
|
|
|
final String iconPath = Platform.isMacOS
|
|
? 'assets/images/tray_icon.png'
|
|
: 'assets/images/tray_icon.ico';
|
|
await trayManager.setIcon(iconPath);
|
|
|
|
// 初始化托盘
|
|
Future.delayed(const Duration(seconds: 1), () async {
|
|
final Menu menu = Menu(
|
|
items: [
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.openDashboard,
|
|
onClick: (_) => _showWindow(),
|
|
),
|
|
if (Platform.isMacOS) MenuItem.separator(),
|
|
if (Platform.isMacOS)
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.copyToTerminal,
|
|
onClick: (_) => _copyToTerminal(),
|
|
),
|
|
MenuItem.separator(),
|
|
MenuItem(
|
|
label: AppTranslations.kr_tray.exitApp,
|
|
onClick: (_) => _exitApp(),
|
|
),
|
|
],
|
|
);
|
|
await trayManager.setContextMenu(menu);
|
|
});
|
|
}
|
|
|
|
/// 复制到终端
|
|
Future<void> _copyToTerminal() async {
|
|
final String kr_port = KRSingBoxImp.instance.kr_port.toString();
|
|
final String proxyText =
|
|
'export https_proxy=http://127.0.0.1:$kr_port http_proxy=http://127.0.0.1:$kr_port all_proxy=socks5://127.0.0.1:$kr_port';
|
|
|
|
await Clipboard.setData(ClipboardData(text: proxyText));
|
|
}
|
|
|
|
/// 退出应用
|
|
Future<void> _exitApp() async {
|
|
KRLogUtil.kr_i('_exitApp: 退出应用');
|
|
await _handleTerminate();
|
|
await windowManager.destroy();
|
|
}
|
|
|
|
/// 显示窗口
|
|
Future<void> _showWindow() async {
|
|
KRLogUtil.kr_i('_showWindow: 开始显示窗口');
|
|
try {
|
|
await windowManager.show();
|
|
await windowManager.focus();
|
|
await windowManager.setSkipTaskbar(false);
|
|
await windowManager.setAlwaysOnTop(true);
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
await windowManager.setAlwaysOnTop(false);
|
|
KRLogUtil.kr_i('_showWindow: 窗口显示成功');
|
|
} catch (e) {
|
|
KRLogUtil.kr_e('_showWindow: 显示窗口失败 - $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onWindowEvent(String eventName) {
|
|
KRLogUtil.kr_i('onWindowEvent: 收到窗口事件 - $eventName');
|
|
// 移除 Windows 下自动显示窗口的逻辑
|
|
}
|
|
|
|
@override
|
|
void onWindowClose() async {
|
|
if (Platform.isWindows) {
|
|
await windowManager.hide();
|
|
} else if (Platform.isMacOS) {
|
|
await windowManager.hide();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onTrayIconMouseDown() {
|
|
// 左键点击只显示菜单
|
|
trayManager.popUpContextMenu();
|
|
}
|
|
|
|
@override
|
|
void onTrayIconRightMouseDown() {
|
|
// 右键点击只显示菜单
|
|
trayManager.popUpContextMenu();
|
|
}
|
|
|
|
@override
|
|
void onWindowFocus() {
|
|
// 移除自动显示窗口的逻辑
|
|
}
|
|
|
|
@override
|
|
void onWindowBlur() {
|
|
// 当窗口失去焦点时,保持窗口可见
|
|
}
|
|
|
|
/// 设置窗口背景颜色
|
|
Future<void> kr_setBackgroundColor(Color color) async {
|
|
await windowManager.setBackgroundColor(color);
|
|
}
|
|
|
|
/// 处理应用终止
|
|
Future<void> _handleTerminate() async {
|
|
KRLogUtil.kr_i('_handleTerminate: 处理应用终止');
|
|
if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) {
|
|
await KRSingBoxImp.instance.kr_stop();
|
|
}
|
|
await trayManager.destroy();
|
|
}
|
|
}
|