1 package etk
2
3 import (
4 "image"
5 "image/color"
6
7 "code.rocket9labs.com/tslocum/etk/messeji"
8 "github.com/hajimehoshi/ebiten/v2"
9 "golang.org/x/image/font"
10 "golang.org/x/image/font/sfnt"
11 )
12
13
14 type Button struct {
15 *Box
16 field *messeji.TextField
17 textFont *sfnt.Font
18 textSize int
19 textAutoSize int
20 borderSize int
21 borderTop color.RGBA
22 borderRight color.RGBA
23 borderBottom color.RGBA
24 borderLeft color.RGBA
25 onSelected func() error
26 pressed bool
27 }
28
29
30 func NewButton(label string, onSelected func() error) *Button {
31 textColor := Style.ButtonTextColor
32 if textColor.A == 0 {
33 textColor = Style.TextColorDark
34 }
35 f := newText()
36 f.SetText(label)
37 f.SetForegroundColor(textColor)
38 f.SetHorizontal(messeji.AlignCenter)
39 f.SetVertical(messeji.AlignCenter)
40 f.SetScrollBarVisible(false)
41
42 b := &Button{
43 Box: NewBox(),
44 field: f,
45 textFont: Style.TextFont,
46 textSize: Scale(Style.TextSize),
47 onSelected: onSelected,
48 borderSize: Scale(Style.BorderSize),
49 borderTop: Style.BorderColorTop,
50 borderRight: Style.BorderColorRight,
51 borderBottom: Style.BorderColorBottom,
52 borderLeft: Style.BorderColorLeft,
53 }
54 b.SetBackground(Style.ButtonBgColor)
55 b.resizeFont()
56 return b
57 }
58
59
60 func (b *Button) SetRect(r image.Rectangle) {
61 b.Box.rect = r
62
63 b.field.SetRect(r)
64 b.resizeFont()
65
66 for _, w := range b.children {
67 w.SetRect(r)
68 }
69 }
70
71
72 func (b *Button) SetBorderSize(size int) {
73 b.Lock()
74 defer b.Unlock()
75
76 b.borderSize = size
77 }
78
79
80 func (b *Button) SetBorderColors(top color.RGBA, right color.RGBA, bottom color.RGBA, left color.RGBA) {
81 b.Lock()
82 defer b.Unlock()
83
84 b.borderTop = top
85 b.borderRight = right
86 b.borderBottom = bottom
87 b.borderLeft = left
88 }
89
90
91 func (b *Button) Text() string {
92 b.Lock()
93 defer b.Unlock()
94
95 return b.field.Text()
96 }
97
98
99 func (b *Button) SetText(text string) {
100 b.Lock()
101 defer b.Unlock()
102
103 b.field.SetText(text)
104 b.resizeFont()
105 }
106
107
108 func (b *Button) SetFont(fnt *sfnt.Font, size int) {
109 b.Lock()
110 defer b.Unlock()
111
112 b.textFont, b.textSize = fnt, size
113 b.resizeFont()
114 }
115
116 func (b *Button) resizeFont() {
117 w, h := b.rect.Dx()-b.field.Padding()*2, b.rect.Dy()-b.field.Padding()*2
118 if w == 0 || h == 0 {
119 if b.textAutoSize == b.textSize {
120 return
121 }
122 b.textAutoSize = b.textSize
123 ff := FontFace(b.textFont, b.textSize)
124 b.field.SetFont(ff, fontMutex)
125 return
126 }
127
128 var autoSize int
129 var ff font.Face
130 for autoSize = b.textSize; autoSize > 0; autoSize-- {
131 ff = FontFace(b.textFont, autoSize)
132 bounds := BoundString(ff, b.field.Text())
133 if bounds.Dx() <= w && bounds.Dy() <= h {
134 break
135 }
136 }
137 if b.textAutoSize == autoSize {
138 return
139 }
140
141 b.field.SetFont(ff, fontMutex)
142 b.textAutoSize = autoSize
143 }
144
145
146 func (b *Button) SetHorizontal(h Alignment) {
147 b.Lock()
148 defer b.Unlock()
149
150 b.field.SetHorizontal(messeji.Alignment(h))
151 }
152
153
154 func (b *Button) SetVertical(v Alignment) {
155 b.Lock()
156 defer b.Unlock()
157
158 b.field.SetVertical(messeji.Alignment(v))
159 }
160
161
162
163 func (b *Button) Cursor() ebiten.CursorShapeType {
164 return ebiten.CursorShapePointer
165 }
166
167
168 func (b *Button) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
169 return false, nil
170 }
171
172
173 func (b *Button) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
174 if !clicked {
175 if b.pressed && !pressed {
176 b.Lock()
177 b.pressed = false
178 b.background = Style.ButtonBgColor
179 b.Unlock()
180 }
181 return true, nil
182 }
183
184 b.Lock()
185 b.pressed = true
186 b.background = color.RGBA{uint8(float64(Style.ButtonBgColor.R) * 0.95), uint8(float64(Style.ButtonBgColor.G) * 0.95), uint8(float64(Style.ButtonBgColor.B) * 0.95), 255}
187 onSelected := b.onSelected
188 if onSelected == nil {
189 b.Unlock()
190 return true, nil
191 }
192 b.Unlock()
193
194 return true, onSelected()
195 }
196
197
198 func (b *Button) Draw(screen *ebiten.Image) error {
199 r := b.rect
200
201
202 b.field.Draw(screen)
203
204
205 if b.borderSize != 0 {
206 if !b.pressed {
207 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+b.borderSize, r.Max.Y)).(*ebiten.Image).Fill(b.borderLeft)
208 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+b.borderSize)).(*ebiten.Image).Fill(b.borderTop)
209 screen.SubImage(image.Rect(r.Max.X-b.borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderRight)
210 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-b.borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderBottom)
211 } else {
212 screen.SubImage(image.Rect(r.Max.X-b.borderSize, r.Min.Y, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderLeft)
213 screen.SubImage(image.Rect(r.Min.X, r.Max.Y-b.borderSize, r.Max.X, r.Max.Y)).(*ebiten.Image).Fill(b.borderTop)
214 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Min.X+b.borderSize, r.Max.Y)).(*ebiten.Image).Fill(b.borderRight)
215 screen.SubImage(image.Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+b.borderSize)).(*ebiten.Image).Fill(b.borderBottom)
216 }
217 }
218
219 return nil
220 }
221
View as plain text