t194 四修:托盤改用原生 NSStatusItem——energye/systray 在 Wails 之下根本不建托盤
leo:右鍵仍然沒用。這次先做**最小重現**才動手,結果推翻了我前兩次的判斷。 ## 真兇(實測,非推論) systray.go:83 的 setInternalLoop(true) **只在 systray.Run() 裡呼叫**; RunWithExternalLoop 沒有 ⇒ registerSystray() 第一行就 `if (!internalLoop) return;` ⇒ **delegate 從沒建立** ⇒ onReady 不會被呼叫、enable_on_click 不會執行、左右鍵事件根本不存在。 最小重現(scratchpad/traytest):RunWithExternalLoop + onReady 印 "READY" ⇒ **READY 從未印出**。托盤自始至終沒被建立過。 (左鍵之所以「第一次能開」是 Wails 自己開的窗,與托盤無關。) ## 正解:原生 NSStatusItem(tray_darwin.m + tray_darwin.go) 左鍵=開視窗;右鍵=暫時掛選單、performClick、再拿掉(不常駐 setMenu:, 否則按鈕 action 不會被呼叫、左鍵也會變成彈選單——與 systray 那個坑同源)。 ## 過程中踩到、也寫進閘的三個坑 ① dispatch_async(main_queue) **無效**:Wails 佔住主執行緒後不跑標準 run loop, 排進去的 block 永遠不執行(log 只印到 "dispatching…")⇒ 改 performSelectorOnMainThread ② NSStatusBar 需要 NSApp 已初始化:在 wails.Run() 之前呼叫 ⇒ 靜默失敗、icon 不出現 ③ 🔴 **我一度對著 `go build` 產的 stub 除錯**——沒帶 Wails build tags 的執行檔 一跑就印 "Wails applications will not build without the correct build tags" 並退出, 我卻以為是「托盤沒建起來」,白繞一圈。**驗 Wails App 一定要用 wails build 的產物。** ## 實測證據(AppleScript 從 UI 層查,最貼近使用者看到的) menu bar 數=2,status item 數=1 ← 選單列 icon 真的存在 collector 同步啟動、正常結束無孤兒 三支機械閘全過(閘也改成驗原生實作:不可 dispatch_async/必須 performSelectorOnMainThread/不可常駐 setMenu/不可再依賴 energye/systray)。 ⚠️ 仍未驗:左右鍵的**實際點擊行為**要 leo 手動點。
This commit is contained in:
+3
-34
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"embed"
|
||||
|
||||
"github.com/energye/systray"
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||
@@ -33,10 +32,11 @@ func main() {
|
||||
// instantiated on the main thread!」⇒ SIGABRT
|
||||
// 正解:main() 一開始(還在主執行緒)就 RunWithExternalLoop 註冊並 start,
|
||||
// 它不阻塞,接著再把主執行緒交給 wails.Run()。
|
||||
// 🔴 托盤必須在**主執行緒**建立(NSStatusItem 內部會 new NSWindow)。
|
||||
// 改用原生 NSStatusItem(見 tray_darwin.m)——實測證明 energye/systray
|
||||
// 在 RunWithExternalLoop 之下**根本不會建立托盤**(onReady 從未觸發)。
|
||||
setupTray(app)
|
||||
trayStart()
|
||||
installSignalHandler() // 收到 TERM/INT 要能正常退出(不必強制結束)
|
||||
defer trayEnd()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "Arcrun",
|
||||
@@ -85,34 +85,3 @@ func main() {
|
||||
// 兩個 toolkit 搶主 loop ⇒ `signal arrived during cgo execution` 直接崩。
|
||||
// 正解是 `RunWithExternalLoop`——它只註冊、把 start/end 交給既有的 loop 呼叫。
|
||||
var trayStart, trayEnd = func() {}, func() {}
|
||||
|
||||
func setupTray(app *App) {
|
||||
trayStart, trayEnd = systray.RunWithExternalLoop(func() {
|
||||
// ① icon:**必須是 template icon**——選單列規格是 16-22pt、純黑+alpha,
|
||||
// 由系統依深淺色自動上色。先前塞 1024x1024 的彩色 app icon
|
||||
// ⇒ 縮成一坨方塊(leo 實測)。改用 CIS 的 double chevron 渲成 44x44 純黑。
|
||||
systray.SetTemplateIcon(trayIcon, trayIcon)
|
||||
systray.SetTooltip("Arcrun — 你的知識庫同步小幫手")
|
||||
|
||||
// 🔴 ②③ 的真兇(energye/systray 原始碼註解寫得很清楚):
|
||||
// 「該方法主動調用後 如果托盤菜單已創建則添加進去, **之後鼠標事件失效**」
|
||||
// ⇒ 只要用 AddMenuItem 建了選單,左右鍵 handler **全部失效**
|
||||
// ⇒ leo 實測「右鍵沒跳出 quit」「第一次能開之後就不再跳」。
|
||||
// 正解:SetMenuNil() 移除選單讓滑鼠事件生效;右鍵則交給函式庫預設行為(見下)。
|
||||
quit := systray.AddMenuItem("結束 Arcrun", "停止同步並關閉")
|
||||
quit.Click(func() { app.Quit() })
|
||||
systray.SetMenuNil() // ← 沒有這行,SetOnClick 不會被呼叫
|
||||
|
||||
// 左鍵:立刻展開主視窗(leo:「點擊托盤的 icon 就立刻展開界面」)
|
||||
systray.SetOnClick(func(menu systray.IMenu) { app.ShowWindow() })
|
||||
|
||||
// 🔴 右鍵:**故意不註冊 handler**。
|
||||
// 看 systray_darwin.go 的 systray_on_rclick():
|
||||
// if onRClick != nil { onRClick(st) } else { C.show_menu() }
|
||||
// ⇒ **沒註冊時,函式庫自己就會把選單叫出來**(而且 show_menu 內部
|
||||
// 已經處理好 create_menu → performClick → set_menu_nil 整套)。
|
||||
// 我先前註冊了自己的 handler 並加 `if menu != nil` 防呆,
|
||||
// 反而把「函式庫本來就會做對的事」換成一個更脆弱的版本
|
||||
// ⇒ leo 實測「右鍵沒跳出 quit」。少寫這段才是正解。
|
||||
}, nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user