passgo/cmd/passgo-gui/ui.go
Greg Pomerantz e7218b1efd Complete the Gio v0.10.2 GUI migration (working on Android).
- Event loop: call w.Event() and FrameEvent.Frame() on the same
  goroutine; no channel bridge (the bridge broke frame rendering).
- clip: use clip.UniformRRect(...).Push(gtx.Ops) / Pop() for real
  clipping in layoutRRect (old .Add(ops) painted unclipped).
- Window clear color is white in v0.10.2.
- Fonts: always provide gofont; on Android use
  WithCollection(gofont.Collection()).
- Button labels: explicitly set Color (zero-value NRGBA has A=0,
  i.e. transparent, so labels were invisible).
- Filter: detect changes by comparing the editor text, since
  material.Editor.Layout consumes the editor's input events
  internally and a later FilterEd.Update(gtx) never sees them.
- idPage: persist the manually entered ID with store.SetID (on
  Android nativeIdentities is unimplemented, so there are no id
  buttons to click).
- Use U+2026 (gofont) instead of U+22EE (not in gofont) for the
  menu button.
- Split getConfDir/noidLabelText out into impl_linux.go for the
  Linux build.
2026-08-21 13:00:35 -04:00

145 lines
3.0 KiB
Go

package main
import (
"image"
"image/color"
"gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/text"
"gioui.org/unit"
"gioui.org/widget/material"
)
var (
black = color.NRGBA{A: 0xff, R: 0, G: 0, B: 0}
white = color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
gray = color.NRGBA{A: 0xff, R: 0xf0, G: 0xf0, B: 0xf0}
darkgray = color.NRGBA{A: 0xff, R: 0xa0, G: 0xa0, B: 0xa0}
)
type Overlay struct {
Size unit.Sp
Text string
Click gesture.Click
Color color.NRGBA
Background color.NRGBA
Alignment text.Alignment
}
func (b *Overlay) Layout(gtx C) D {
ins := layout.UniformInset(unit.Dp(1))
return ins.Layout(gtx, func(gtx C) D {
st := layout.Stack{}
c2 := layout.Stacked(func(gtx C) D {
l := material.Label(th, b.Size, b.Text)
ins := layout.UniformInset(unit.Dp(4))
l.Color = b.Color
return ins.Layout(gtx, func(gtx C) D {
return l.Layout(gtx)
})
})
c1 := layout.Expanded(func(gtx C) D {
return layoutRRect(b.Background, gtx)
})
return st.Layout(gtx, c1, c2)
})
}
type SelButton struct {
Button
SelColor color.NRGBA
Selected bool
}
type Button struct {
Size unit.Sp
Label string
Click gesture.Click
Color color.NRGBA
Background color.NRGBA
Alignment text.Alignment
clicked bool
}
func layoutRRect(col color.NRGBA, gtx C) D {
r := gtx.Dp(4)
sz := image.Point{X: gtx.Constraints.Min.X, Y: gtx.Constraints.Min.Y}
// Push the rounded-rect clip, paint, then restore the previous clip.
stk := clip.UniformRRect(image.Rectangle{Max: sz}, r).Push(gtx.Ops)
paint.ColorOp{Color: col}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
stk.Pop()
return layout.Dimensions{Size: sz}
}
func (b *Button) Layout(gtx C) D {
mwidth := gtx.Constraints.Min.X
b.clicked = false
ins := layout.UniformInset(unit.Dp(1))
return ins.Layout(gtx, func(gtx C) D {
st := layout.Stack{}
c2 := layout.Stacked(func(gtx C) D {
l := material.Label(th, b.Size, b.Label)
l.Color = b.Color
ins := layout.UniformInset(unit.Dp(4))
ret := ins.Layout(gtx, func(gtx C) D {
return l.Layout(gtx)
})
// Register the click handler for the label area.
stk := clip.Rect(image.Rectangle{Max: ret.Size}).Push(gtx.Ops)
event.Op(gtx.Ops, b)
b.Click.Add(gtx.Ops)
stk.Pop()
for {
ev, ok := b.Click.Update(gtx.Source)
if !ok {
break
}
if ev.Kind == gesture.KindClick {
b.clicked = true
}
}
return ret
})
c1 := layout.Expanded(func(gtx C) D {
gtx.Constraints.Min.X = mwidth
return layoutRRect(b.Background, gtx)
})
return st.Layout(gtx, c1, c2)
})
}
func (b *Button) Clicked() bool {
return b.clicked
}
func (b *SelButton) Toggle() {
b.Selected = !b.Selected
b.SelColor, b.Background = b.Background, b.SelColor
}
func (b *SelButton) Select() {
if !b.Selected {
b.Toggle()
}
}
func (b *SelButton) Deselect() {
if b.Selected {
b.Toggle()
}
}
func (b *SelButton) Clicked() bool {
if b.clicked {
b.Toggle()
return true
} else {
return false
}
}