fit.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package gel
4
5 import (
6 "image"
7
8 "github.com/p9c/gio/f32"
9 l "github.com/p9c/gio/layout"
10 "github.com/p9c/gio/op"
11 "github.com/p9c/gio/op/clip"
12 )
13
14 // Fit scales a widget to fit and clip to the constraints.
15 type Fit uint8
16
17 const (
18 // Unscaled does not alter the scale of a widget.
19 Unscaled Fit = iota
20 // Contain scales widget as large as possible without cropping
21 // and it preserves aspect-ratio.
22 Contain
23 // Cover scales the widget to cover the constraint area and
24 // preserves aspect-ratio.
25 Cover
26 // ScaleDown scales the widget smaller without cropping,
27 // when it exceeds the constraint area.
28 // It preserves aspect-ratio.
29 ScaleDown
30 // Stretch stretches the widget to the constraints and does not
31 // preserve aspect-ratio.
32 Stretch
33 )
34
35 // scale adds clip and scale operations to fit dims to the constraints.
36 // It positions the widget to the appropriate position.
37 // It returns dimensions modified accordingly.
38 func (fit Fit) scale(gtx l.Context, pos l.Direction, dims l.Dimensions) l.Dimensions {
39 widgetSize := dims.Size
40
41 if fit == Unscaled || dims.Size.X == 0 || dims.Size.Y == 0 {
42 dims.Size = gtx.Constraints.Constrain(dims.Size)
43 clip.Rect{Max: dims.Size}.Add(gtx.Ops)
44
45 offset := pos.Position(widgetSize, dims.Size)
46 op.Offset(l.FPt(offset)).Add(gtx.Ops)
47 dims.Baseline += offset.Y
48 return dims
49 }
50
51 scale := f32.Point{
52 X: float32(gtx.Constraints.Max.X) / float32(dims.Size.X),
53 Y: float32(gtx.Constraints.Max.Y) / float32(dims.Size.Y),
54 }
55
56 switch fit {
57 case Contain:
58 if scale.Y < scale.X {
59 scale.X = scale.Y
60 } else {
61 scale.Y = scale.X
62 }
63 case Cover:
64 if scale.Y > scale.X {
65 scale.X = scale.Y
66 } else {
67 scale.Y = scale.X
68 }
69 case ScaleDown:
70 if scale.Y < scale.X {
71 scale.X = scale.Y
72 } else {
73 scale.Y = scale.X
74 }
75
76 // The widget would need to be scaled up, no change needed.
77 if scale.X >= 1 {
78 dims.Size = gtx.Constraints.Constrain(dims.Size)
79 clip.Rect{Max: dims.Size}.Add(gtx.Ops)
80
81 offset := pos.Position(widgetSize, dims.Size)
82 op.Offset(l.FPt(offset)).Add(gtx.Ops)
83 dims.Baseline += offset.Y
84 return dims
85 }
86 case Stretch:
87 }
88
89 var scaledSize image.Point
90 scaledSize.X = int(float32(widgetSize.X) * scale.X)
91 scaledSize.Y = int(float32(widgetSize.Y) * scale.Y)
92 dims.Size = gtx.Constraints.Constrain(scaledSize)
93 dims.Baseline = int(float32(dims.Baseline) * scale.Y)
94
95 clip.Rect{Max: dims.Size}.Add(gtx.Ops)
96
97 offset := pos.Position(scaledSize, dims.Size)
98 op.Affine(f32.Affine2D{}.
99 Scale(f32.Point{}, scale).
100 Offset(l.FPt(offset)),
101 ).Add(gtx.Ops)
102
103 dims.Baseline += offset.Y
104
105 return dims
106 }
107