...
1 package etk
2
3 import (
4 "image"
5 "image/color"
6
7 "github.com/hajimehoshi/ebiten/v2"
8 "github.com/hajimehoshi/ebiten/v2/vector"
9 )
10
11
12 type Checkbox struct {
13 *Box
14
15 selected bool
16 checkColor color.RGBA
17 borderSize int
18 borderColor color.RGBA
19 img *ebiten.Image
20 onSelect func() error
21 }
22
23
24 func NewCheckbox(onSelect func() error) *Checkbox {
25 c := &Checkbox{
26 Box: NewBox(),
27 checkColor: Style.TextColorDark,
28 borderSize: 2,
29 borderColor: Style.ButtonBorderBottom,
30 onSelect: onSelect,
31 }
32 c.SetBackground(Style.CheckboxBgColor)
33 return c
34 }
35
36
37
38 func (c *Checkbox) SetRect(r image.Rectangle) {
39 if c.Box.rect.Eq(r) {
40 return
41 }
42
43 bounds := r.Bounds()
44 newSize := bounds.Dx()
45 if bounds.Dy() < newSize {
46 newSize = bounds.Dy()
47 }
48
49 if r.Dx() != newSize {
50 r.Max.X = r.Min.X + newSize
51 }
52 if r.Dy() != newSize {
53 r.Max.Y = r.Min.Y + newSize
54 }
55 c.Box.rect = r
56
57 c.updateImage()
58
59 for _, w := range c.children {
60 w.SetRect(r)
61 }
62 }
63
64
65 func (c *Checkbox) SetCheckColor(checkColor color.RGBA) {
66 c.checkColor = checkColor
67 c.updateImage()
68 }
69
70
71 func (c *Checkbox) SetBorderColor(borderColor color.RGBA) {
72 c.borderColor = borderColor
73 c.updateImage()
74 }
75
76
77 func (c *Checkbox) Selected() bool {
78 return c.selected
79 }
80
81
82
83 func (c *Checkbox) SetSelected(selected bool) {
84 if c.selected == selected {
85 return
86 }
87 c.selected = selected
88 c.updateImage()
89 }
90
91
92
93 func (c *Checkbox) Cursor() ebiten.CursorShapeType {
94 return ebiten.CursorShapePointer
95 }
96
97
98 func (c *Checkbox) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
99 return false, nil
100 }
101
102
103 func (c *Checkbox) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
104 if !clicked {
105 return true, nil
106 }
107
108 c.selected = !c.selected
109 c.updateImage()
110
111 c.Lock()
112 onSelect := c.onSelect
113 if onSelect == nil {
114 c.Unlock()
115 return true, nil
116 }
117 c.Unlock()
118
119 return true, onSelect()
120 }
121
122 func (c *Checkbox) updateImage() {
123 r := c.Rect()
124 if r.Empty() {
125 c.img = nil
126 return
127 }
128
129 bounds := r.Bounds()
130 newSize := bounds.Dx()
131 var initializeImg bool
132 if c.img == nil {
133 initializeImg = true
134 } else {
135 imgBounds := c.img.Bounds()
136 imgSize := imgBounds.Dx()
137 if imgSize != newSize {
138 initializeImg = true
139 }
140 }
141 if initializeImg {
142 c.img = ebiten.NewImage(newSize, newSize)
143 }
144
145
146 c.img.Fill(c.borderColor)
147 c.img.SubImage(rectAtOrigin(r).Inset(c.borderSize)).(*ebiten.Image).Fill(color.RGBA64{0, 0, 0, 0})
148
149
150 if !c.selected {
151 return
152 }
153 strokeOp := &vector.StrokeOptions{}
154 strokeOp.LineJoin = vector.LineJoinRound
155 strokeOp.LineCap = vector.LineCapButt
156 strokeOp.Width = float32(c.borderSize)
157
158 strokePathOp := &vector.DrawPathOptions{}
159 strokePathOp.AntiAlias = true
160 strokePathOp.ColorScale.ScaleWithColor(c.checkColor)
161
162 path := &vector.Path{}
163 path.MoveTo(0, 0)
164 path.LineTo(float32(r.Dx()), float32(r.Dy()))
165 path.MoveTo(0, float32(r.Dy()))
166 path.LineTo(float32(r.Dx()), 0)
167 vector.StrokePath(c.img, path, strokeOp, strokePathOp)
168 }
169
170
171 func (c *Checkbox) Draw(screen *ebiten.Image) error {
172 if c.img == nil {
173 return nil
174 }
175
176 op := &ebiten.DrawImageOptions{}
177 op.GeoM.Translate(float64(c.rect.Min.X), float64(c.rect.Min.Y))
178 screen.DrawImage(c.img, op)
179 return nil
180 }
181
View as plain text