From aced262f6bc1eb903c24341f686522492fbf3668 Mon Sep 17 00:00:00 2001 From: ila Date: Thu, 23 Jul 2026 22:00:44 +0800 Subject: [PATCH] feat(v2): T-309 no startup foreground grab, auto-close popup, rename buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Comment out Gio's startup SetForegroundWindow/SetFocus in the vendored os_windows.go (marked SILVER-POSE PATCH; reverts on re-vendor) so the window no longer steals foreground on launch. Gio has no option for this. - The fall confirm popup auto-closes 5s after opening (op.InvalidateCmd schedules the redraw even with no live frames); '我已知晓' still closes early. - Rename save buttons to 保存摄像头参数 / 保存常用检测参数. Cross-compiles for Windows. Co-Authored-By: Claude Opus 4.8 --- progress.md | 9 +++++++++ v2/internal/ui/window.go | 18 ++++++++++++++++-- v2/vendor/gioui.org/app/os_windows.go | 6 ++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/progress.md b/progress.md index 7ee9ffd..ba13e33 100644 --- a/progress.md +++ b/progress.md @@ -639,3 +639,12 @@ - 验证:UI 用 `GOOS=windows CGO_ENABLED=0` 交叉编译;store(sqlite,cgo) 本地 `CGO_ENABLED=1` gcc 编译;窗口/弹窗/DB 目视需 Windows。 - 决策:SQLite 用 cgo mattn(用户选,vendor 更小;V2 本就 cgo)。DB 只存内网 host/端口/通道,不存密码;DB 放 artifacts/ 下随 gitignore。先做 ①(取消置顶/抢前台)。 - 下一步:定位并取消窗口置顶(Gio ViewEvent 拿 HWND + SetWindowPos NOTOPMOST/NOACTIVATE),交叉编译验证。 + +## 【2026-07-23】T-309 A:取消抢前台 + 弹窗自动关 + 按钮改名 + +- 状态:DOING +- 变更:①**vendored patch**——注释掉 `v2/vendor/gioui.org/app/os_windows.go` 启动时的 `SetForegroundWindow`/`SetFocus`(带 `SILVER-POSE PATCH (T-309)` 标记),取消"启动抢前台"。**注意:Gio 无 API 可关此行为;重跑 `go mod vendor` 会还原此补丁,需按标记重打。** 窗口默认非置顶(TopMost=false),故不需另改置顶。②④ 保存按钮改名为"保存摄像头参数"/"保存常用检测参数"。⑤ 摔倒确认弹窗记录打开时间,`layout` 中超 5 秒自动清除,并用 `op.InvalidateCmd{At:}` 调度到期重绘(停流也能自动关);"我已知晓"仍可提前关。 +- 验证:`GOOS=windows CGO_ENABLED=0 go build -mod=vendor ./internal/ui/... ./cmd/ui-spike/...` 退出码 0(含 patched os_windows.go);gofmt 干净。窗口不抢前台/弹窗自动关的实际行为需 Windows 目视。 +- 阻塞:③测试连接、⑥SQLite 未做。 +- 决策:抢前台只能靠 patch vendored Gio(无 API);已加显著标记并在此记录,便于 re-vendor 后重打。 +- 下一步:③测试连接摄像头按钮+弹窗;⑥SQLite(mattn) 记录(config 保留 host/端口/通道 + internal/store + 接线)。 diff --git a/v2/internal/ui/window.go b/v2/internal/ui/window.go index a3ac1b9..9c7123e 100644 --- a/v2/internal/ui/window.go +++ b/v2/internal/ui/window.go @@ -115,6 +115,7 @@ type Window struct { latestImage *image.RGBA imageGen uint64 pending *alert.Record + pendingAt time.Time seenAlerts map[string]struct{} incoming []galleryIncoming seenEvents map[string]struct{} @@ -335,6 +336,7 @@ func (window *Window) PresentAlert(record alert.Record) { window.seenAlerts[record.Event.EventID] = struct{}{} queued := record window.pending = &queued + window.pendingAt = time.Now() window.mu.Unlock() playAlertSound() window.appWindow.Invalidate() @@ -360,6 +362,7 @@ func (window *Window) layout(gtx layout.Context) layout.Dimensions { frame := window.latestImage gen := window.imageGen pending := window.pending + pendingAt := window.pendingAt incoming := window.incoming running := window.running window.incoming = nil @@ -410,6 +413,17 @@ func (window *Window) layout(gtx layout.Context) layout.Dimensions { window.mu.Unlock() pending = nil } + // The confirm popup auto-closes 5 seconds after it opens. + if pending != nil { + if time.Since(pendingAt) >= 5*time.Second { + window.mu.Lock() + window.pending = nil + window.mu.Unlock() + pending = nil + } else { + gtx.Execute(op.InvalidateCmd{At: pendingAt.Add(5 * time.Second)}) + } + } content := func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, @@ -701,7 +715,7 @@ func (window *Window) layoutSettings(gtx layout.Context, model ViewModel) layout window.editorRow("传输 (tcp/udp)", &window.editTransport), window.editorRow("连接超时 (秒)", &window.editTimeout), window.checkRow("低延迟", &window.lowLatency), - window.buttonRow(&window.saveCamera, "保存摄像头"), + window.buttonRow(&window.saveCamera, "保存摄像头参数"), ) paramsCard := verticalRows( window.heading("常用检测参数(下次启动生效)"), @@ -711,7 +725,7 @@ func (window *Window) layoutSettings(gtx layout.Context, model ViewModel) layout window.editorRow("模型置信度", &window.editModelConf), window.checkRow("要求先快速下移", &window.requireRapid), window.checkRow("要求膝踝清晰", &window.requireLower), - window.buttonRow(&window.saveParams, "保存参数"), + window.buttonRow(&window.saveParams, "保存常用检测参数"), window.labelRow(window.settingsStatus), ) depsCard := verticalRows( diff --git a/v2/vendor/gioui.org/app/os_windows.go b/v2/vendor/gioui.org/app/os_windows.go index 8af2d6a..a5d5ca0 100644 --- a/v2/vendor/gioui.org/app/os_windows.go +++ b/v2/vendor/gioui.org/app/os_windows.go @@ -112,8 +112,10 @@ func newWindow(win *callbacks, options []Option) { defer winMap.Delete(w.hwnd) w.Configure(options) w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)}) - windows.SetForegroundWindow(w.hwnd) - windows.SetFocus(w.hwnd) + // SILVER-POSE PATCH (T-309): do not steal foreground/focus on startup. + // Re-applying `go mod vendor` reverts this; see progress.md. + // windows.SetForegroundWindow(w.hwnd) + // windows.SetFocus(w.hwnd) // Since the window class for the cursor is null, // set it here to show the cursor. w.SetCursor(pointer.CursorDefault)