fix(client/windows): 任务栏图标 + 关闭窗口不再断连接
- 关闭窗口断连接:onExitRequested 原先无条件 _teardownVpn 再退出;Windows 点 X 会触发该回调拆隧道,而 window_manager 的 preventClose 又把窗口仅隐藏到托盘 → 出现「一关就断网、进程还在」。改为 Windows 下点 X/Alt+F4 取消退出+隐藏, 保持隧道;真正退出只走托盘「退出」(onBeforeQuit 标记 _isQuitting 再拆隧道)。 macOS Cmd+Q 仍正常拆隧道退出。 - 任务栏图标没换:仅靠 WNDCLASS.hIcon(LoadIcon 单尺寸)任务栏可能拿不到正确 尺寸。窗口创建后用 LoadImage 按系统 SM_CXICON/SM_CXSMICON 各取一份, WM_SETICON 显式设 ICON_BIG(任务栏/Alt-Tab)+ ICON_SMALL(标题栏)。 - 版本 1.0.1 → 1.0.2。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+16
-2
@@ -46,17 +46,31 @@ class PangolinApp extends ConsumerStatefulWidget {
|
||||
|
||||
class _PangolinAppState extends ConsumerState<PangolinApp> {
|
||||
AppLifecycleListener? _lifecycle;
|
||||
// 是否正在「真退出」(托盘「退出」菜单触发)。区分:点关闭只隐藏到托盘、保持隧道。
|
||||
bool _isQuitting = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (isDesktop) {
|
||||
// 真退出(Cmd+Q / 托盘退出)前先停内核拆隧道,避免 sing-box 孤儿 + TUN 残留。
|
||||
// 关闭 / 退出请求的处理:
|
||||
// - Windows:点 X / Alt+F4 会触发 onExitRequested,但桌面端要常驻托盘、隧道不断
|
||||
// → 取消退出并隐藏到托盘。真正退出只走托盘「退出」菜单(见 onBeforeQuit)。
|
||||
// (否则会出现:连接被拆、窗口却只是隐藏,即「关掉就断网但进程还在」。)
|
||||
// - macOS:Cmd+Q 等真退出 → 先停内核拆隧道再退出(红叉关闭走 onWindowClose 隐藏)。
|
||||
_lifecycle = AppLifecycleListener(onExitRequested: () async {
|
||||
if (Platform.isWindows && !_isQuitting) {
|
||||
await windowManager.hide();
|
||||
return ui.AppExitResponse.cancel;
|
||||
}
|
||||
await _teardownVpn();
|
||||
return ui.AppExitResponse.exit;
|
||||
});
|
||||
TrayService.instance.onBeforeQuit = _teardownVpn;
|
||||
// 托盘「退出」前置钩子:标记真退出 + 停内核拆隧道(避免 sing-box 孤儿 + TUN 残留)。
|
||||
TrayService.instance.onBeforeQuit = () async {
|
||||
_isQuitting = true;
|
||||
await _teardownVpn();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
name: pangolin_vpn
|
||||
description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。
|
||||
publish_to: "none"
|
||||
version: 1.0.1+2
|
||||
version: 1.0.2+3
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.0
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在
|
||||
; ..\..\build\windows\x64\runner\Release
|
||||
#define MyAppName "穿山甲 Pangolin"
|
||||
#define MyAppVersion "1.0.1"
|
||||
#define MyAppVersion "1.0.2"
|
||||
#define MyAppPublisher "Pangolin"
|
||||
#define MyAppExeName "pangolin_vpn.exe"
|
||||
#define BuildDir "..\..\build\windows\x64\runner\Release"
|
||||
|
||||
@@ -144,6 +144,25 @@ bool Win32Window::Create(const std::wstring& title,
|
||||
return false;
|
||||
}
|
||||
|
||||
// 显式给窗口设大/小两种图标:任务栏 / Alt-Tab 用 ICON_BIG(32px),标题栏用
|
||||
// ICON_SMALL(16px)。仅靠 WNDCLASS.hIcon(LoadIcon 取单一尺寸)时,任务栏可能
|
||||
// 拿不到正确尺寸而沿用旧/默认图标 —— 从多尺寸 app_icon.ico 按系统尺寸各取一份。
|
||||
HINSTANCE instance = GetModuleHandle(nullptr);
|
||||
HANDLE big_icon = LoadImage(instance, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON,
|
||||
GetSystemMetrics(SM_CXICON),
|
||||
GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
|
||||
HANDLE small_icon = LoadImage(instance, MAKEINTRESOURCE(IDI_APP_ICON), IMAGE_ICON,
|
||||
GetSystemMetrics(SM_CXSMICON),
|
||||
GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
|
||||
if (big_icon) {
|
||||
SendMessage(window, WM_SETICON, ICON_BIG,
|
||||
reinterpret_cast<LPARAM>(big_icon));
|
||||
}
|
||||
if (small_icon) {
|
||||
SendMessage(window, WM_SETICON, ICON_SMALL,
|
||||
reinterpret_cast<LPARAM>(small_icon));
|
||||
}
|
||||
|
||||
UpdateTheme(window);
|
||||
|
||||
return OnCreate();
|
||||
|
||||
Reference in New Issue
Block a user