160 lines
4.1 KiB
Go
160 lines
4.1 KiB
Go
// Command gio-spike validates a Gio window for the V2 UI, bypassing the Walk
|
||||
|
|
// TTM_ADDTOOL startup failure (Gio uses no Win32 common controls, so the
|
|||
|
|
// manifest/comctl issue cannot occur). It shows a synthetic RGBA frame — the
|
|||
|
|
// same *image.RGBA type internal/render produces — a top dual-tab bar and a
|
|||
|
|
// start/stop button, then closes cleanly.
|
|||
|
|
//
|
|||
|
|
// Run on Windows: go run ./cmd/gio-spike
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"image"
|
|||
|
|
"image/color"
|
|||
|
|
"log"
|
|||
|
|
"os"
|
|||
|
|
|
|||
|
|
"gioui.org/app"
|
|||
|
|
"gioui.org/font/gofont"
|
|||
|
|
"gioui.org/layout"
|
|||
|
|
"gioui.org/op"
|
|||
|
|
"gioui.org/op/paint"
|
|||
|
|
"gioui.org/text"
|
|||
|
|
"gioui.org/unit"
|
|||
|
|
"gioui.org/widget"
|
|||
|
|
"gioui.org/widget/material"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
go func() {
|
|||
|
|
w := new(app.Window)
|
|||
|
|
w.Option(
|
|||
|
|
app.Title("Silver Pose V2 · Gio Spike"),
|
|||
|
|
app.Size(unit.Dp(960), unit.Dp(600)),
|
|||
|
|
)
|
|||
|
|
if err := loop(w); err != nil {
|
|||
|
|
log.Fatal(err)
|
|||
|
|
}
|
|||
|
|
os.Exit(0)
|
|||
|
|
}()
|
|||
|
|
app.Main()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type spikeUI struct {
|
|||
|
|
theme *material.Theme
|
|||
|
|
frame paint.ImageOp
|
|||
|
|
tabs []string
|
|||
|
|
tabButton []widget.Clickable
|
|||
|
|
active int
|
|||
|
|
startStop widget.Clickable
|
|||
|
|
running bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func newSpikeUI() *spikeUI {
|
|||
|
|
theme := material.NewTheme()
|
|||
|
|
theme.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
|
|||
|
|
ui := &spikeUI{
|
|||
|
|
theme: theme,
|
|||
|
|
frame: paint.NewImageOp(syntheticFrame(640, 360)),
|
|||
|
|
tabs: []string{"实时监控", "设置"},
|
|||
|
|
}
|
|||
|
|
ui.tabButton = make([]widget.Clickable, len(ui.tabs))
|
|||
|
|
return ui
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func loop(w *app.Window) error {
|
|||
|
|
ui := newSpikeUI()
|
|||
|
|
var ops op.Ops
|
|||
|
|
for {
|
|||
|
|
switch e := w.Event().(type) {
|
|||
|
|
case app.DestroyEvent:
|
|||
|
|
return e.Err
|
|||
|
|
case app.FrameEvent:
|
|||
|
|
gtx := app.NewContext(&ops, e)
|
|||
|
|
ui.layout(gtx)
|
|||
|
|
e.Frame(gtx.Ops)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (ui *spikeUI) layout(gtx layout.Context) layout.Dimensions {
|
|||
|
|
paint.Fill(gtx.Ops, color.NRGBA{R: 0xEA, G: 0xF1, B: 0xF8, A: 0xFF})
|
|||
|
|
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
|||
|
|
layout.Rigid(ui.layoutTabs),
|
|||
|
|
layout.Flexed(1, ui.layoutBody),
|
|||
|
|
layout.Rigid(ui.layoutControls),
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (ui *spikeUI) layoutTabs(gtx layout.Context) layout.Dimensions {
|
|||
|
|
for i := range ui.tabButton {
|
|||
|
|
if ui.tabButton[i].Clicked(gtx) {
|
|||
|
|
ui.active = i
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
children := make([]layout.FlexChild, 0, len(ui.tabs))
|
|||
|
|
for i := range ui.tabs {
|
|||
|
|
i := i
|
|||
|
|
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
|||
|
|
button := material.Button(ui.theme, &ui.tabButton[i], ui.tabs[i])
|
|||
|
|
if i != ui.active {
|
|||
|
|
button.Background = color.NRGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0xFF}
|
|||
|
|
button.Color = color.NRGBA{R: 0x52, G: 0x65, B: 0x7C, A: 0xFF}
|
|||
|
|
}
|
|||
|
|
return layout.UniformInset(unit.Dp(6)).Layout(gtx, button.Layout)
|
|||
|
|
}))
|
|||
|
|
}
|
|||
|
|
return layout.Flex{}.Layout(gtx, children...)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (ui *spikeUI) layoutBody(gtx layout.Context) layout.Dimensions {
|
|||
|
|
if ui.active == 0 {
|
|||
|
|
return layout.UniformInset(unit.Dp(8)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
|||
|
|
return widget.Image{
|
|||
|
|
Src: ui.frame,
|
|||
|
|
Fit: widget.Contain,
|
|||
|
|
Position: layout.Center,
|
|||
|
|
}.Layout(gtx)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
return layout.Center.Layout(gtx, material.Body1(ui.theme, "设置页占位(spike)").Layout)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (ui *spikeUI) layoutControls(gtx layout.Context) layout.Dimensions {
|
|||
|
|
if ui.startStop.Clicked(gtx) {
|
|||
|
|
ui.running = !ui.running
|
|||
|
|
}
|
|||
|
|
label := "开始监控"
|
|||
|
|
if ui.running {
|
|||
|
|
label = "停止监控"
|
|||
|
|
}
|
|||
|
|
return layout.UniformInset(unit.Dp(8)).Layout(gtx, material.Button(ui.theme, &ui.startStop, label).Layout)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// syntheticFrame stands in for internal/render's *image.RGBA output: a gradient
|
|||
|
|
// with a 6px red border, mimicking a CONFIRMED overlay, to prove image display.
|
|||
|
|
func syntheticFrame(width, height int) *image.RGBA {
|
|||
|
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
|||
|
|
for y := 0; y < height; y++ {
|
|||
|
|
for x := 0; x < width; x++ {
|
|||
|
|
img.SetRGBA(x, y, color.RGBA{
|
|||
|
|
R: uint8(x * 255 / width),
|
|||
|
|
G: uint8(y * 255 / height),
|
|||
|
|
B: 90,
|
|||
|
|
A: 255,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
red := color.RGBA{R: 198, G: 40, B: 40, A: 255}
|
|||
|
|
for t := 0; t < 6; t++ {
|
|||
|
|
for x := 0; x < width; x++ {
|
|||
|
|
img.SetRGBA(x, t, red)
|
|||
|
|
img.SetRGBA(x, height-1-t, red)
|
|||
|
|
}
|
|||
|
|
for y := 0; y < height; y++ {
|
|||
|
|
img.SetRGBA(t, y, red)
|
|||
|
|
img.SetRGBA(width-1-t, y, red)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return img
|
|||
|
|
}
|