Files
2026-10-28 17:39:33 +08:00

151 lines
3.2 KiB
Go

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 := &regionFrame{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()
}