mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
widget: make Fit.scale a pure function
Most importantly, return dimensions and transformation instead of adding operations. Makes the function easier to test, and supports scoped transform and clip stacks. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+12
-23
@@ -7,8 +7,6 @@ import (
|
|||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
|
||||||
"gioui.org/op/clip"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fit scales a widget to fit and clip to the constraints.
|
// Fit scales a widget to fit and clip to the constraints.
|
||||||
@@ -32,25 +30,21 @@ const (
|
|||||||
Fill
|
Fill
|
||||||
)
|
)
|
||||||
|
|
||||||
// scale adds clip and scale operations to fit dims to the constraints.
|
// scale computes the new dimensions and transformation required to fit dims to cs, given the position.
|
||||||
// It positions the widget to the appropriate position.
|
func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Dimensions) (layout.Dimensions, f32.Affine2D) {
|
||||||
// It returns dimensions modified accordingly.
|
|
||||||
func (fit Fit) scale(gtx layout.Context, pos layout.Direction, dims layout.Dimensions) layout.Dimensions {
|
|
||||||
widgetSize := dims.Size
|
widgetSize := dims.Size
|
||||||
|
|
||||||
if fit == Unscaled || dims.Size.X == 0 || dims.Size.Y == 0 {
|
if fit == Unscaled || dims.Size.X == 0 || dims.Size.Y == 0 {
|
||||||
dims.Size = gtx.Constraints.Constrain(dims.Size)
|
dims.Size = cs.Constrain(dims.Size)
|
||||||
clip.Rect{Max: dims.Size}.Add(gtx.Ops)
|
|
||||||
|
|
||||||
offset := pos.Position(widgetSize, dims.Size)
|
offset := pos.Position(widgetSize, dims.Size)
|
||||||
op.Offset(layout.FPt(offset)).Add(gtx.Ops)
|
|
||||||
dims.Baseline += offset.Y
|
dims.Baseline += offset.Y
|
||||||
return dims
|
return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
scale := f32.Point{
|
scale := f32.Point{
|
||||||
X: float32(gtx.Constraints.Max.X) / float32(dims.Size.X),
|
X: float32(cs.Max.X) / float32(dims.Size.X),
|
||||||
Y: float32(gtx.Constraints.Max.Y) / float32(dims.Size.Y),
|
Y: float32(cs.Max.Y) / float32(dims.Size.Y),
|
||||||
}
|
}
|
||||||
|
|
||||||
switch fit {
|
switch fit {
|
||||||
@@ -75,13 +69,11 @@ func (fit Fit) scale(gtx layout.Context, pos layout.Direction, dims layout.Dimen
|
|||||||
|
|
||||||
// The widget would need to be scaled up, no change needed.
|
// The widget would need to be scaled up, no change needed.
|
||||||
if scale.X >= 1 {
|
if scale.X >= 1 {
|
||||||
dims.Size = gtx.Constraints.Constrain(dims.Size)
|
dims.Size = cs.Constrain(dims.Size)
|
||||||
clip.Rect{Max: dims.Size}.Add(gtx.Ops)
|
|
||||||
|
|
||||||
offset := pos.Position(widgetSize, dims.Size)
|
offset := pos.Position(widgetSize, dims.Size)
|
||||||
op.Offset(layout.FPt(offset)).Add(gtx.Ops)
|
|
||||||
dims.Baseline += offset.Y
|
dims.Baseline += offset.Y
|
||||||
return dims
|
return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
|
||||||
}
|
}
|
||||||
case Fill:
|
case Fill:
|
||||||
}
|
}
|
||||||
@@ -89,18 +81,15 @@ func (fit Fit) scale(gtx layout.Context, pos layout.Direction, dims layout.Dimen
|
|||||||
var scaledSize image.Point
|
var scaledSize image.Point
|
||||||
scaledSize.X = int(float32(widgetSize.X) * scale.X)
|
scaledSize.X = int(float32(widgetSize.X) * scale.X)
|
||||||
scaledSize.Y = int(float32(widgetSize.Y) * scale.Y)
|
scaledSize.Y = int(float32(widgetSize.Y) * scale.Y)
|
||||||
dims.Size = gtx.Constraints.Constrain(scaledSize)
|
dims.Size = cs.Constrain(scaledSize)
|
||||||
dims.Baseline = int(float32(dims.Baseline) * scale.Y)
|
dims.Baseline = int(float32(dims.Baseline) * scale.Y)
|
||||||
|
|
||||||
clip.Rect{Max: dims.Size}.Add(gtx.Ops)
|
|
||||||
|
|
||||||
offset := pos.Position(scaledSize, dims.Size)
|
offset := pos.Position(scaledSize, dims.Size)
|
||||||
op.Affine(f32.Affine2D{}.
|
trans := f32.Affine2D{}.
|
||||||
Scale(f32.Point{}, scale).
|
Scale(f32.Point{}, scale).
|
||||||
Offset(layout.FPt(offset)),
|
Offset(layout.FPt(offset))
|
||||||
).Add(gtx.Ops)
|
|
||||||
|
|
||||||
dims.Baseline += offset.Y
|
dims.Baseline += offset.Y
|
||||||
|
|
||||||
return dims
|
return dims, trans
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-29
@@ -3,15 +3,11 @@
|
|||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"image"
|
"image"
|
||||||
"math"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFit(t *testing.T) {
|
func TestFit(t *testing.T) {
|
||||||
@@ -81,26 +77,13 @@ func TestFit(t *testing.T) {
|
|||||||
for fit, tests := range fittests {
|
for fit, tests := range fittests {
|
||||||
fit := Fit(fit)
|
fit := Fit(fit)
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
ops := new(op.Ops)
|
cs := layout.Constraints{
|
||||||
gtx := layout.Context{
|
Max: image.Point{X: 100, Y: 100},
|
||||||
Ops: ops,
|
|
||||||
Constraints: layout.Constraints{
|
|
||||||
Max: image.Point{X: 100, Y: 100},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
result, trans := fit.scale(cs, layout.NW, layout.Dimensions{Size: test.Dims})
|
||||||
result := fit.scale(gtx, layout.NW, layout.Dimensions{Size: test.Dims})
|
sx, _, _, _, sy, _ := trans.Elems()
|
||||||
|
if scale := f32.Pt(sx, sy); scale != test.Scale {
|
||||||
if test.Scale.X != 1 || test.Scale.Y != 1 {
|
t.Errorf("got scale %v expected %v", scale, test.Scale)
|
||||||
opsdata := gtx.Ops.Data()
|
|
||||||
scaleX := float32Bytes(test.Scale.X)
|
|
||||||
scaleY := float32Bytes(test.Scale.Y)
|
|
||||||
if !bytes.Contains(opsdata, scaleX) {
|
|
||||||
t.Errorf("did not find scale.X:%v (%x) in ops: %x", test.Scale.X, scaleX, opsdata)
|
|
||||||
}
|
|
||||||
if !bytes.Contains(opsdata, scaleY) {
|
|
||||||
t.Errorf("did not find scale.Y:%v (%x) in ops: %x", test.Scale.Y, scaleY, opsdata)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Size != test.Result {
|
if result.Size != test.Result {
|
||||||
@@ -109,9 +92,3 @@ func TestFit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func float32Bytes(v float32) []byte {
|
|
||||||
var dst [4]byte
|
|
||||||
binary.LittleEndian.PutUint32(dst[:], math.Float32bits(v))
|
|
||||||
return dst[:]
|
|
||||||
}
|
|
||||||
|
|||||||
+5
-2
@@ -8,6 +8,7 @@ import (
|
|||||||
"gioui.org/f32"
|
"gioui.org/f32"
|
||||||
"gioui.org/layout"
|
"gioui.org/layout"
|
||||||
"gioui.org/op"
|
"gioui.org/op"
|
||||||
|
"gioui.org/op/clip"
|
||||||
"gioui.org/op/paint"
|
"gioui.org/op/paint"
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
)
|
)
|
||||||
@@ -42,10 +43,12 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions {
|
|||||||
wf, hf := float32(size.X), float32(size.Y)
|
wf, hf := float32(size.X), float32(size.Y)
|
||||||
w, h := gtx.Px(unit.Dp(wf*scale)), gtx.Px(unit.Dp(hf*scale))
|
w, h := gtx.Px(unit.Dp(wf*scale)), gtx.Px(unit.Dp(hf*scale))
|
||||||
|
|
||||||
dims := im.Fit.scale(gtx, im.Position, layout.Dimensions{Size: image.Pt(w, h)})
|
dims, trans := im.Fit.scale(gtx.Constraints, im.Position, layout.Dimensions{Size: image.Pt(w, h)})
|
||||||
|
clip.Rect{Max: dims.Size}.Add(gtx.Ops)
|
||||||
|
|
||||||
pixelScale := scale * gtx.Metric.PxPerDp
|
pixelScale := scale * gtx.Metric.PxPerDp
|
||||||
op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale))).Add(gtx.Ops)
|
trans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale)))
|
||||||
|
op.Affine(trans).Add(gtx.Ops)
|
||||||
|
|
||||||
im.Src.Add(gtx.Ops)
|
im.Src.Add(gtx.Ops)
|
||||||
paint.PaintOp{}.Add(gtx.Ops)
|
paint.PaintOp{}.Add(gtx.Ops)
|
||||||
|
|||||||
Reference in New Issue
Block a user