128 lines
2.5 KiB
Go
128 lines
2.5 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)}
|
|
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()})
|
|
}
|