package ui import ( "log/slog" "sync" "viewsagent/internal/capture" "viewsagent/internal/win32" ) // regionFrame 是常驻的取景框提示。 // // 屏幕上一直显示当前框选区域的红框,让使用者随时知道自己在看哪一块; // 这个框被 SetWindowDisplayAffinity 排除在截屏与录屏之外, // 所以模型看到的画面、录屏软件录到的画面里都不会有它。 // // 红框本身是可以交互的:拖动边框可以改范围,右上角有「确定 / 取消」两个 // 按钮(平时全透明,鼠标移上去才显示),用来直接修改监视区域。 type regionFrame struct { frame *win32.Frame mu sync.Mutex last capture.Region hasLast bool suspended bool onApply func(capture.Region) onCancel func() } // newRegionFrame 创建取景框。创建失败时返回 nil,调用方自行决定是否降级。 func newRegionFrame() *regionFrame { f, err := win32.NewFrame() if err != nil { slog.Warn("取景框创建失败,屏幕将不再显示选区边框", "err", err) return nil } r := ®ionFrame{frame: f} f.SetCallbacks(r.commit, r.abort) return r } // commit 是取景框上点「确定」的回调:把调整后的区域交给上层。 func (f *regionFrame) commit(r win32.Rect) { if f == nil || f.onApply == nil { return } f.onApply(capture.Region{X: r.Left, Y: r.Top, W: r.Width(), H: r.Height()}) } // abort 是取景框上点「取消」的回调:红框已经自己隐藏了。 func (f *regionFrame) abort() { if f == nil || f.onCancel == nil { return } f.onCancel() } // apply 把取景框移到指定区域;区域无效时隐藏。 func (f *regionFrame) apply(r capture.Region) { if f == nil { return } f.mu.Lock() if f.suspended { f.mu.Unlock() return } if f.hasLast && f.last == r { f.mu.Unlock() return } f.last = r f.hasLast = true f.mu.Unlock() if !r.Valid() { f.frame.Hide() return } f.frame.SetRegion(win32.Rect{ Left: r.X, Top: r.Y, Right: r.X + r.W, Bottom: r.Y + r.H, }) } // suspend 临时隐藏取景框(框选过程中全屏有选区浮层,避免两层边框打架)。 func (f *regionFrame) suspend() { if f == nil { return } f.mu.Lock() f.suspended = true f.hasLast = false f.mu.Unlock() f.frame.Hide() } // resume 恢复取景框,并按当前区域重新落位。 func (f *regionFrame) resume(r capture.Region) { if f == nil { return } f.mu.Lock() f.suspended = false f.mu.Unlock() f.apply(r) } // show 重新显示取景框(会清掉"用户点过取消"的状态)。 func (f *regionFrame) show() { if f == nil { return } f.frame.Show() } // toggle 切换取景框的显示状态,返回切换后是否可见。 func (f *regionFrame) toggle() bool { if f == nil { return false } return f.frame.ToggleVisible() } // visible 返回取景框当前是否显示。 func (f *regionFrame) visible() bool { if f == nil { return false } return f.frame.Visible() } // close 销毁取景框窗口。 func (f *regionFrame) close() { if f == nil { return } f.frame.Close() } // Debug 返回取景框内部状态,供自检使用。 func (f *regionFrame) Debug() string { if f == nil || f.frame == nil { return "取景框未创建" } return f.frame.Debug() }