-
Notifications
You must be signed in to change notification settings - Fork 0
/
linearlayout.go
264 lines (234 loc) · 6.5 KB
/
linearlayout.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
255
256
257
258
259
260
261
262
263
264
package layout
import (
"encoding/xml"
"github.com/bhollier/ui/pkg/ui/element"
"github.com/bhollier/ui/pkg/ui/util"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"math"
"net/http"
)
// Layout type for displaying elements as a
// list (either vertically or horizontally)
type LinearLayout struct {
// A linear layout is an element
element.Impl
// It is also a layout
element.LayoutImpl
// The element's orientation
Orientation util.Orientation `uixml:"http://github.com/bhollier/ui/api/schema orientation,optional"`
}
// Function to create a new linear layout
func NewLinearLayout(fs http.FileSystem, name xml.Name, parent element.Layout) element.Element {
return &LinearLayout{
Impl: element.NewElement(fs, name, parent),
Orientation: util.DefaultOrientation,
}
}
// The XML name of the element
var LinearLayoutTypeName = xml.Name{Space: "http://github.com/bhollier/ui/api/schema", Local: "LinearLayout"}
// Function to unmarshal an XML element into
// an element. This function is usually only
// called by xml.Unmarshal
func (e *LinearLayout) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) {
// Unmarshal the element part of the layout
err = e.Impl.UnmarshalXML(d, start)
if err != nil {
return err
}
// Set the element's attributes
err = element.SetAttrs(e, start.Attr)
if err != nil {
return err
}
// Unmarshal the layout's children
e.LayoutImpl.Children, err = element.ChildrenUnmarshalXML(e.GetFS(), e, d, start)
if err != nil {
return err
}
return nil
}
// Function to reset the element's
// position
func (e *LinearLayout) ResetPosition() {
e.Impl.ResetPosition()
e.LayoutImpl.ResetPosition()
}
// Function to reset the element
func (e *LinearLayout) Reset() {
e.Impl.Reset()
e.LayoutImpl.Reset()
}
// Function to determine whether
// the element is initialised
func (e *LinearLayout) IsInitialised() bool {
return e.Impl.IsInitialised() &&
element.ChildrenAreInitialised(e)
}
// Function to initialise the element
func (e *LinearLayout) Init(window *pixelgl.Window, bounds *pixel.Rect) error {
// If the layout's width isn't known and
// the width is meant to match the content size
if e.GetActualWidth() == nil && e.GetRelWidth().MatchContent {
// If the orientation is horizontal
if e.Orientation == util.HorizontalOrientation {
var width float64
if e.GetPadding().Unit == util.Pixels {
width = float64(e.GetPadding().Quantity * 2)
}
allChildrenInit := true
for i := 0; i < e.NumChildren(); i++ {
if e.GetChild(i).GetActualWidth() == nil {
allChildrenInit = false
break
}
// Add the child's width to the width
width += *e.GetChild(i).GetActualWidth()
}
// If all the children were considered,
// set the actual width
if allChildrenInit {
e.SetActualWidth(&width)
}
} else {
var maxWidth float64
if e.GetPadding().Unit == util.Pixels {
maxWidth = float64(e.GetPadding().Quantity * 2)
}
allChildrenInit := true
for i := 0; i < e.NumChildren(); i++ {
if e.GetChild(i).GetActualWidth() == nil {
allChildrenInit = false
break
}
// Get the max width
maxWidth = math.Max(maxWidth, *e.GetChild(i).GetActualWidth())
}
// If all the children were considered,
// set the actual width
if allChildrenInit {
e.SetActualWidth(&maxWidth)
}
}
}
// If the layout's height isn't known and
// the height is meant to match the content size
if e.GetActualHeight() == nil && e.GetRelHeight().MatchContent {
// If the orientation is horizontal
if e.Orientation == util.VerticalOrientation {
var height float64
if e.GetPadding().Unit == util.Pixels {
height = float64(e.GetPadding().Quantity * 2)
}
allChildrenInit := true
for i := 0; i < e.NumChildren(); i++ {
if e.GetChild(i).GetActualHeight() == nil {
allChildrenInit = false
break
}
// Add the child's height to the height
height += *e.GetChild(i).GetActualHeight()
}
// If all the children were considered,
// set the actual height
if allChildrenInit {
e.SetActualHeight(&height)
}
} else {
var maxHeight float64
if e.GetPadding().Unit == util.Pixels {
maxHeight = float64(e.GetPadding().Quantity * 2)
}
allChildrenInit := true
for i := 0; i < e.NumChildren(); i++ {
if e.GetChild(i).GetActualHeight() == nil {
allChildrenInit = false
break
}
// Get the max height
maxHeight = math.Max(maxHeight, *e.GetChild(i).GetActualHeight())
}
// If all the children were considered,
// set the actual height
if allChildrenInit {
e.SetActualHeight(&maxHeight)
}
}
}
// Initialise the element part of the layout
err := e.Impl.Init(window, bounds)
if err != nil {
return err
}
// The child's position
var childPos *pixel.Vec
// If the minimum is known
if bounds != nil &&
e.GetMin() != nil &&
e.GetMax() != nil {
// Get the padding
var padding float64
if e.GetPadding().Unit == util.Pixels {
padding = float64(e.GetPadding().Quantity)
}
// Set the position
childPos = &pixel.Vec{
X: e.GetMin().X + padding,
Y: e.GetMax().Y + padding,
}
} else {
childPos = nil
}
// Initialise the children
var child element.Element
for i := 0; i < e.NumChildren(); i++ {
child = e.GetChild(i)
// If the child hasn't been initialised yet
if !child.IsInitialised() {
// The child's bounds (default is nil)
var childBounds *pixel.Rect
// If the child's position can be calculated
// (if the previous position is known and the
// child's width/height is known)
if childPos != nil &&
child.GetActualWidth() != nil &&
child.GetActualHeight() != nil {
// Calculate the child's size
childSize := pixel.V(*child.GetActualWidth(),
*child.GetActualHeight())
// Minus the Y by the size of the child
childPos.Y -= childSize.Y
// Set the child bounds
childBounds = &pixel.Rect{
Min: *childPos,
Max: childPos.Add(childSize),
}
// Increase childPos (for the next child)
if e.Orientation == util.HorizontalOrientation {
childPos.X += childSize.X
}
} else {
childPos = nil
}
// Initialise the child
err := child.Init(window, childBounds)
if err != nil {
return err
}
}
}
return nil
}
// Function that is called when there
// is a new event
func (e *LinearLayout) NewEvent(window *pixelgl.Window) {
e.Impl.NewEvent(window)
e.LayoutImpl.NewEvent(window)
}
// Function to draw the element
func (e *LinearLayout) Draw() {
// Draw the element
e.Impl.Draw()
// Draw the layout
element.DrawLayout(e)
}