refactor(tracker/gioui): avoid heap escapes in NumericUpDown

This commit is contained in:
5684185+vsariola@users.noreply.github.com
2025-06-23 09:43:10 +03:00
parent db2ccf977d
commit 31007515b5
6 changed files with 103 additions and 90 deletions

View File

@ -23,33 +23,53 @@ import (
"gioui.org/text"
)
type NumericUpDown struct {
DpPerStep unit.Dp
type (
NumericUpDownState struct {
DpPerStep unit.Dp
dragStartValue int
dragStartXY float32
clickDecrease gesture.Click
clickIncrease gesture.Click
tipArea component.TipArea
dragStartValue int
dragStartXY float32
clickDecrease gesture.Click
clickIncrease gesture.Click
tipArea component.TipArea
}
NumericUpDownStyle struct {
TextColor color.NRGBA `yaml:",flow"`
IconColor color.NRGBA `yaml:",flow"`
BgColor color.NRGBA `yaml:",flow"`
CornerRadius unit.Dp
ButtonWidth unit.Dp
Width unit.Dp
Height unit.Dp
TextSize unit.Sp
Font font.Font
}
NumericUpDown struct {
Int tracker.Int
Theme *Theme
State *NumericUpDownState
Style *NumericUpDownStyle
Tip string
}
)
func NewNumericUpDownState() *NumericUpDownState {
return &NumericUpDownState{DpPerStep: unit.Dp(8)}
}
type NumericUpDownStyle struct {
TextColor color.NRGBA `yaml:",flow"`
IconColor color.NRGBA `yaml:",flow"`
BgColor color.NRGBA `yaml:",flow"`
CornerRadius unit.Dp
ButtonWidth unit.Dp
Width unit.Dp
Height unit.Dp
TextSize unit.Sp
Font font.Font
func NumUpDown(v tracker.Int, th *Theme, n *NumericUpDownState, tip string) NumericUpDown {
return NumericUpDown{
Int: v,
Theme: th,
State: n,
Style: &th.NumericUpDown,
Tip: tip,
}
}
func NewNumericUpDown() *NumericUpDown {
return &NumericUpDown{DpPerStep: unit.Dp(8)}
}
func (s *NumericUpDown) Update(gtx layout.Context, v tracker.Int) {
func (s *NumericUpDownState) Update(gtx layout.Context, v tracker.Int) {
// handle dragging
pxPerStep := float32(gtx.Dp(s.DpPerStep))
for {
@ -86,31 +106,23 @@ func (s *NumericUpDown) Update(gtx layout.Context, v tracker.Int) {
}
}
func (s *NumericUpDown) Widget(v tracker.Int, th *Theme, st *NumericUpDownStyle, tooltip string) func(gtx C) D {
return func(gtx C) D {
return s.Layout(gtx, v, th, st, tooltip)
func (n *NumericUpDown) Layout(gtx C) D {
n.State.Update(gtx, n.Int)
if n.Tip != "" {
return n.State.tipArea.Layout(gtx, Tooltip(n.Theme, n.Tip), n.actualLayout)
}
return n.actualLayout(gtx)
}
func (s *NumericUpDown) Layout(gtx C, v tracker.Int, th *Theme, st *NumericUpDownStyle, tooltip string) D {
s.Update(gtx, v)
if tooltip != "" {
return s.tipArea.Layout(gtx, Tooltip(th, tooltip), func(gtx C) D {
return s.actualLayout(gtx, v, th, st)
})
}
return s.actualLayout(gtx, v, th, st)
}
func (s *NumericUpDown) actualLayout(gtx C, v tracker.Int, th *Theme, st *NumericUpDownStyle) D {
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(st.Width), gtx.Dp(st.Height)))
width := gtx.Dp(st.ButtonWidth)
height := gtx.Dp(st.Height)
func (n *NumericUpDown) actualLayout(gtx C) D {
gtx.Constraints = layout.Exact(image.Pt(gtx.Dp(n.Style.Width), gtx.Dp(n.Style.Height)))
width := gtx.Dp(n.Style.ButtonWidth)
height := gtx.Dp(n.Style.Height)
return layout.Background{}.Layout(gtx,
func(gtx C) D {
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(st.CornerRadius)).Push(gtx.Ops).Pop()
paint.Fill(gtx.Ops, st.BgColor)
event.Op(gtx.Ops, s) // register drag inputs, if not hitting the clicks
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, gtx.Dp(n.Style.CornerRadius)).Push(gtx.Ops).Pop()
paint.Fill(gtx.Ops, n.Style.BgColor)
event.Op(gtx.Ops, n.State) // register drag inputs, if not hitting the clicks
return D{Size: gtx.Constraints.Min}
},
func(gtx C) D {
@ -120,25 +132,25 @@ func (s *NumericUpDown) actualLayout(gtx C, v tracker.Int, th *Theme, st *Numeri
return layout.Background{}.Layout(gtx,
func(gtx C) D {
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop()
s.clickDecrease.Add(gtx.Ops)
n.State.clickDecrease.Add(gtx.Ops)
return D{Size: gtx.Constraints.Min}
},
func(gtx C) D { return th.Icon(icons.ContentRemove).Layout(gtx, st.IconColor) },
func(gtx C) D { return n.Theme.Icon(icons.ContentRemove).Layout(gtx, n.Style.IconColor) },
)
}),
layout.Flexed(1, func(gtx C) D {
paint.ColorOp{Color: st.TextColor}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, th.Material.Shaper, st.Font, st.TextSize, strconv.Itoa(v.Value()), op.CallOp{})
paint.ColorOp{Color: n.Style.TextColor}.Add(gtx.Ops)
return widget.Label{Alignment: text.Middle}.Layout(gtx, n.Theme.Material.Shaper, n.Style.Font, n.Style.TextSize, strconv.Itoa(n.Int.Value()), op.CallOp{})
}),
layout.Rigid(func(gtx C) D {
gtx.Constraints = layout.Exact(image.Pt(width, height))
return layout.Background{}.Layout(gtx,
func(gtx C) D {
defer clip.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Push(gtx.Ops).Pop()
s.clickIncrease.Add(gtx.Ops)
n.State.clickIncrease.Add(gtx.Ops)
return D{Size: gtx.Constraints.Min}
},
func(gtx C) D { return th.Icon(icons.ContentAdd).Layout(gtx, st.IconColor) },
func(gtx C) D { return n.Theme.Icon(icons.ContentAdd).Layout(gtx, n.Style.IconColor) },
)
}),
)