/
button.go
227 lines (199 loc) · 6.04 KB
/
button.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package element
import (
"encoding/xml"
"github.com/bhollier/ui/pkg/ui/util"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"log"
"net/http"
)
// A button's state
type ButtonState string
// The default button state
const ButtonDefaultState = "default"
// The button state when the mouse is over it
const ButtonHoveredState = "hovered"
// The button state for when the
// mouse is pressing the button
const ButtonPressedState = "pressed"
// Interface for a button element
type Button interface {
// A button is an element
Element
// Function to get the button's
// current state
GetButtonState() ButtonState
// Function to set the button's
// current state
SetButtonState(s ButtonState)
// Function to get the button's
// background field from XML for
// the given state
GetButtonBkgField(s ButtonState) string
// Function to get the button's
// background sprite for the given
// state
GetButtonBkg(s ButtonState) *pixel.Sprite
// Function to set the button's
// background sprite for the given
// state
SetButtonBkg(state ButtonState, sprite *pixel.Sprite)
// Function to call the press callback
CallPressCallback() error
}
// Type for a button. Note, structs
// must either include ButtonImpl or
// Impl, not both
type ButtonImpl struct {
// The button is an element
Impl
// The button's current state
state ButtonState
// The button's background when
// being hovered over from XML
HoveredBackground string `uixml:"http://github.com/bhollier/ui/api/schema bkg-hovered,optional"`
// The button's background when
// being pressed from XML
PressedBackground string `uixml:"http://github.com/bhollier/ui/api/schema bkg-pressed,optional"`
// The button's background
// sprites for each state
backgrounds map[ButtonState]*pixel.Sprite
// The element's press callback
PressCallback string `uixml:"http://github.com/bhollier/ui/api/schema press-callback,optional"`
}
// Function to create a button element
func NewButton(fs http.FileSystem, name xml.Name, parent Layout) ButtonImpl {
e := ButtonImpl{Impl: NewElement(fs, name, parent)}
// Set the state as the default
e.state = ButtonDefaultState
// Create the backgrounds map
e.backgrounds = map[ButtonState]*pixel.Sprite{
ButtonDefaultState: nil,
ButtonHoveredState: nil,
ButtonPressedState: nil,
}
return e
}
// Function to get the button's
// current state
func (e *ButtonImpl) GetButtonState() ButtonState { return e.state }
// Function to set the button's
// current state
func (e *ButtonImpl) SetButtonState(s ButtonState) {
e.state = s
// Update the background
e.GetBkg().SetSprite(e.backgrounds[e.state])
}
// Function to get the button's
// background field from XML for
// the given state
func (e *ButtonImpl) GetButtonBkgField(s ButtonState) string {
if s == ButtonDefaultState {
return e.GetBkg().GetField()
} else if s == ButtonHoveredState {
return e.HoveredBackground
} else {
return e.PressedBackground
}
}
// Function to get the button's
// background sprite for the given
// state
func (e *ButtonImpl) GetButtonBkg(s ButtonState) *pixel.Sprite { return e.backgrounds[s] }
// Function to set the button's
// background sprite for the given
// state
func (e *ButtonImpl) SetButtonBkg(state ButtonState, sprite *pixel.Sprite) {
e.backgrounds[state] = sprite
}
// Function to call the press callback
func (e *ButtonImpl) CallPressCallback() error {
if e.PressCallback != "" {
return Call(e.PressCallback, e)
}
return nil
}
// Function to determine whether
// the element is initialised
func (e *ButtonImpl) IsInitialised() bool {
return e.Impl.IsInitialised() &&
(e.HoveredBackground == "" || e.backgrounds[ButtonHoveredState] != nil) &&
(e.PressedBackground == "" || e.backgrounds[ButtonPressedState] != nil)
}
// Function to initialise the element
func (e *ButtonImpl) Init(window *pixelgl.Window, bounds *pixel.Rect) error {
// Initialise the element
err := e.Impl.Init(window, bounds)
if err != nil {
return err
}
// If the bounds are known
if bounds != nil {
// If the default background hasn't been set
if e.backgrounds[ButtonDefaultState] == nil {
e.backgrounds[ButtonDefaultState] = e.Impl.GetBkg().GetSprite()
}
// If the hovered background hasn't been made
if e.backgrounds[ButtonHoveredState] == nil {
// Load the background picture
picture, err := util.CreatePictureFromField(e.GetFS(), e.HoveredBackground)
if err != nil {
return err
}
if picture != nil {
// Create a sprite
e.backgrounds[ButtonHoveredState] = pixel.NewSprite(picture, picture.Bounds())
} else {
e.backgrounds[ButtonHoveredState] = e.backgrounds[ButtonDefaultState]
}
}
// If the pressed background hasn't been made
if e.backgrounds[ButtonPressedState] == nil {
// Load the background picture
picture, err := util.CreatePictureFromField(e.GetFS(), e.PressedBackground)
if err != nil {
return err
}
if picture != nil {
e.backgrounds[ButtonPressedState] = pixel.NewSprite(picture, picture.Bounds())
} else {
e.backgrounds[ButtonPressedState] = e.backgrounds[ButtonDefaultState]
}
}
}
return nil
}
// Function to handle a button's new event
func ButtonNewEvent(e Button, window *pixelgl.Window) {
// Whether the button's state changed
stateChange := false
// If the mouse is actually in the window
if window.MouseInsideWindow() &&
e.GetCanvas().Bounds().Contains(window.MousePosition()) {
// If the mouse button is being pressed
if window.Pressed(pixelgl.MouseButtonLeft) {
stateChange = e.GetButtonState() != ButtonPressedState
e.SetButtonState(ButtonPressedState)
// If the state changed
if stateChange {
// Call the press callback
err := e.CallPressCallback()
if err != nil {
// could be better
log.Printf("Error from button callback: %+v", err)
}
}
} else {
stateChange = e.GetButtonState() != ButtonHoveredState
e.SetButtonState(ButtonHoveredState)
}
} else {
stateChange = e.GetButtonState() != ButtonDefaultState
e.SetButtonState(ButtonDefaultState)
}
// If the state was changed
if stateChange {
// Redraw the whole UI
DrawUI(e, window)
}
}