-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontrol.go
254 lines (212 loc) · 5.84 KB
/
control.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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
package main
import (
. "github.com/ahmetb/go-linq"
"github.com/dresswithpockets/go-vgui"
"github.com/faiface/pixel"
"math"
)
type Alignment int
const (
AlignCenter Alignment = iota
AlignLeft
AlignRight
AlignNorth
AlignSouth
AlignWest
AlignEast
AlignNorthWest
AlignNorthEast
AlignSouthWest
AlignSouthEast
)
const FullSize = math.MaxInt16
type RelativeTo int
const (
RelativeLeftOrTop RelativeTo = iota
RelativeRightOrBottom
RelativeCenter
)
type RelativeInt struct {
value int
relativeTo RelativeTo
}
type ControlPosition struct {
x RelativeInt
y RelativeInt
}
type Position struct {
x int
y int
}
type Size struct {
width int16
height int16
}
type Bounds struct {
Position
Size
}
type VguiImage struct {
name string
picture pixel.Picture
}
type ControlBuilder func(object *vgui.Object) Control
type ControlProvider struct {
builders map[string]ControlBuilder
}
type Control interface {
zOrder() int
draw()
drawChildren()
setParent(other Control)
getBounds() pixel.Rect
recalculateBounds()
}
type BaseControl struct {
app *App
name string
fieldName string
pos ControlPosition
z int
size Size
visible bool
enabled bool
fgColor *SchemeColor
bgColor *SchemeColor
border *SchemeBorder
font *SchemeFont
labelText string
textAlignment Alignment
textInset Position
tabPosition int
dullText bool
brightText bool
image *VguiImage
paintBackground bool
paintBorder bool
_default int
parent Control
children []Control
// indicates that we need to recalculate the absolute bounds of this control
dirty bool
absoluteBounds pixel.Rect
baseOverride *BaseControl
}
func (c *ControlProvider) setBuilder(controlName string, builder ControlBuilder) {
c.builders[controlName] = builder
}
func (c *ControlProvider) resolveNewControlFromObject(object *vgui.Object) (Control, error) {
controlName, ok := object.Get("ControlName")
if !ok {
panic("ControlName not found on object when resolving new control from object. Default behaviour not well defined.")
}
if !controlName.IsValue {
panic("ControlName must always be a single value, not an object with properties.")
}
if builder, ok := c.builders[controlName.Value]; ok {
return builder(object), nil
}
return nil, &ErrUnknownControlName{controlName.Value}
}
func (c *BaseControl) zOrder() int {
return c.z
}
// TODO: from vgui.Value
func (c *BaseControl) draw() {
panic("Draw not implemented on the abstract BaseControl.")
}
// drawChildren draws all sub-controls of this control according to their zOrder
//goland:noinspection SpellCheckingInspection
func (c *BaseControl) drawChildren() {
var sortedChildren []Control
From(c.children).Sort(func(a, b interface{}) bool {
actl := a.(Control)
bctl := b.(Control)
return actl.zOrder() < bctl.zOrder()
}).ToSlice(&sortedChildren)
for _, child := range sortedChildren {
child.draw()
child.drawChildren()
}
}
func (c *BaseControl) setParent(other Control) {
if c.parent == other {
return
}
if c.parent != nil {
parentBase := c.parent.(*BaseControl)
// get the index of our control in the parent's children slice and the remove it from the slice
// we have to do it this way in order to maintain order which is at least *somewhat* important for
// controls on the same z-plane
for i, v := range parentBase.children {
if v == c {
parentBase.children = append(parentBase.children[:i], parentBase.children[i+1:]...)
break
}
}
}
// set the parent on our control & append our control to the parent's children list
c.parent = other
base := c.parent.(*BaseControl)
base.children = append(base.children, c)
c.dirty = true
}
func (c *BaseControl) getBounds() pixel.Rect {
if c.dirty {
c.recalculateBounds()
c.dirty = false
}
return c.absoluteBounds
}
func (c *BaseControl) recalculateBounds() {
viewport := c.app.window.Bounds()
if c.parent != nil {
viewport = c.parent.getBounds()
}
parentCenter := viewport.Center()
parentSize := viewport.Size()
left := parentCenter.X - parentSize.X/2
right := parentCenter.X + parentSize.Y/2
top := parentCenter.Y - parentSize.Y/2
bottom := parentCenter.Y + parentSize.Y/2
var xpos, ypos float64
switch c.pos.x.relativeTo {
case RelativeLeftOrTop:
xpos = left
break
case RelativeCenter:
xpos = parentCenter.X
break
case RelativeRightOrBottom:
xpos = right
break
}
switch c.pos.y.relativeTo {
case RelativeLeftOrTop:
ypos = top
break
case RelativeCenter:
ypos = parentCenter.Y
break
case RelativeRightOrBottom:
ypos = bottom
break
}
xpos += float64(c.pos.x.value)
ypos += float64(c.pos.y.value)
c.absoluteBounds = pixel.R(xpos, ypos, xpos+float64(c.size.width), ypos+float64(c.size.height))
}
/*func defaultControlBuilder(t reflect.Type, object *vgui.Object) Control {
var control Control
t.AssignableTo(reflect.TypeOf(control))
value := reflect.New(t)
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
}
// TODO: parse object values into fields
}
func parseIntValue(value string) (reflect.Value, error) {
i, err := strconv.ParseInt(value, 10, 16)
return reflect.ValueOf(i), err
}*/
// TODO parse bools, floats, special string types, etc