Files
deskview/internal/ui/card.go
T
2026-10-28 17:39:33 +08:00

131 lines
2.7 KiB
Go

package ui
import (
"strings"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"viewsagent/internal/capture"
"viewsagent/internal/win32"
)
// cardTitle 是答案浮窗的标题,win32 靠它找 HWND。
const cardTitle = "viewsagent-card"
// floatCard 是贴在选区旁边的置顶小窗,只显示最新答案。
type floatCard struct {
win fyne.Window
body *widget.RichText
region capture.Region
visible bool
}
func newFloatCard(a fyne.App) *floatCard {
c := &floatCard{win: a.NewWindow(cardTitle)}
// 点关闭只是隐藏浮窗:Fyne 的窗口一旦 Close 就被销毁,之后再也 Show
// 不出来,工具栏的按钮会永久失效。
c.win.SetCloseIntercept(func() { c.hide() })
rt := widget.NewRichText()
rt.Wrapping = fyne.TextWrapWord
c.body = rt
c.win.SetContent(container.NewBorder(
nil, nil, nil, nil,
container.NewPadded(rt),
))
c.win.Resize(fyne.NewSize(400, 240))
return c
}
// setRegion 记录当前监视区域,用于定位浮窗。
func (c *floatCard) setRegion(r capture.Region) {
c.region = r
}
// update 刷新浮窗内容。
func (c *floatCard) update(text string) {
text = strings.TrimSpace(text)
if text == "" {
text = "(空)"
}
c.body.ParseMarkdown(text)
if c.visible {
c.place()
}
}
// show 显示浮窗。
func (c *floatCard) show() {
if c.visible {
c.place()
c.win.RequestFocus()
return
}
c.visible = true
c.win.Show()
go func() {
time.Sleep(120 * time.Millisecond)
fyne.Do(c.place)
}()
}
// hide 隐藏浮窗。
func (c *floatCard) hide() {
c.visible = false
c.win.Hide()
}
// toggle 切换显示状态。
func (c *floatCard) toggle() {
if c.visible {
c.hide()
return
}
c.show()
}
// visibleNow 返回是否正在显示。
func (c *floatCard) visibleNow() bool { return c.visible }
// place 把浮窗贴到选区下方(放不下就贴上方),并置顶。
func (c *floatCard) place() {
hwnd, err := win32.FindWindowByTitle(cardTitle)
if err != nil {
return
}
_ = win32.SetTopMost(hwnd)
rect, err := win32.WindowRect(hwnd)
if err != nil || rect.Width() <= 0 || rect.Height() <= 0 {
return
}
if !c.region.Valid() {
return
}
mon, _ := win32.MonitorAt(c.region.X+c.region.W/2, c.region.Y+c.region.H/2)
work := mon.Work
if work.Width() <= 0 {
work = mon.Rect
}
gap := int(8 * max(mon.Scale, 1))
x := c.region.X
y := c.region.Y + c.region.H + gap
if y+rect.Height() > work.Bottom {
y = c.region.Y - rect.Height() - gap
}
if y < work.Top {
y = work.Bottom - rect.Height()
}
if x+rect.Width() > work.Right {
x = work.Right - rect.Width()
}
if x < work.Left {
x = work.Left
}
_ = win32.MoveTo(hwnd, win32.Rect{Left: x, Top: y, Right: x + rect.Width(), Bottom: y + rect.Height()})
}