File size: 22,467 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
XWindowLayoutFuncs = {}
XWindowMeasureFuncs = {}
XWindowLayoutMethods = { "None", "Box", "HOverlappingList", "VOverlappingList", "HList", "VList", "HPanel", "VPanel", "Grid", "HWrap", "VWrap" }


----- None layout

function XWindowMeasureFuncs:None(max_width, max_height)
	return max_width, max_height
end

function XWindowLayoutFuncs:None(x, y, width, height)
end


----- Box layout

function XWindowMeasureFuncs:Box(max_width, max_height)
	local width = 0
	local height = 0
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			width = Max(width, win.measure_width)
			height = Max(height, win.measure_height)
		end
	end
	return width, height
end

function XWindowLayoutFuncs:Box(x, y, width, height)
	for _, win in ipairs(self) do
		if not win.Dock then
			win:SetLayoutSpace(x, y, width, height)
		end
	end
end

----- HOverlappingList layout

function XWindowMeasureFuncs:HOverlappingList(max_width, max_height)
	return XWindowMeasureFuncs.HList(self, max_width, max_height)
end

function XWindowLayoutFuncs:HOverlappingList(x, y, width, height)
	local max_item_width, width_sum, items, last_item = 0, 0, 0
	local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
	for _, win in ipairs(self) do
		if not win.Dock then
			max_item_width = Max(max_item_width, win.measure_width)
			width_sum = width_sum + win.measure_width
			last_item = win
			items = items + 1
		end
	end
	local overflow = 0
	local fill = self.FillOverlappingSpace
	if items > 1 then
		local total_width = (self.UniformColumnWidth and items * max_item_width or width_sum) + (items - 1) * spacing
		overflow = total_width - width
		if not fill then
			overflow = Max(overflow, 0)
		end
	end
	local n = 1
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_width = Min(max_item_width, win.measure_width)
			win:SetLayoutSpace(x, y, item_width, height)
			local move = 0
			if (fill or overflow > 0) and items > 1 then
				move = -(overflow * n / (items - 1) - overflow * (n - 1) / (items - 1))
			end
			x = x + item_width + spacing + move
			n = n + 1
		end
	end
end

----- VOverlappingList layout

function XWindowMeasureFuncs:VOverlappingList(max_width, max_height)
	return XWindowMeasureFuncs.VList(self, max_width, max_height)
end

function XWindowLayoutFuncs:VOverlappingList(x, y, width, height)
	local max_item_height, height_sum, items, last_item = 0, 0, 0
	local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
	for _, win in ipairs(self) do
		if not win.Dock then
			max_item_height = Max(max_item_height, win.measure_height)
			height_sum = height_sum + win.measure_height
			last_item = win
			items = items + 1
		end
	end
	local overflow = 0
	local fill = self.FillOverlappingSpace
	if items > 1 then
		local total_height = (self.UniformRowHeight and items * max_item_height or height_sum) + (items - 1) * spacing
		overflow = total_height - height
		if not fill then
			overflow = Max(total_height - height, 0)
		end
	end
	local n = 1
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_height = Min(max_item_height, win.measure_height)
			win:SetLayoutSpace(x, y, width, item_height)
			local move = 0
			if (fill or overflow > 0) and items > 1 then
				move = -(overflow * n / (items - 1) - overflow * (n - 1) / (items - 1))
			end
			y = y + item_height + spacing + move
			n = n + 1
		end
	end
end

----- HList layout

function XWindowMeasureFuncs:HList(max_width, max_height)
	local item_width, item_height, width_sum, items = 0, 0, 0, 0
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			item_width = Max(item_width, win.measure_width)
			item_height = Max(item_height, win.measure_height)
			width_sum = width_sum + win.measure_width
			items = items + 1
		end
	end
	local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
	return (self.UniformColumnWidth and items * item_width or width_sum) + Max(0, items - 1) * spacing, item_height
end

function XWindowLayoutFuncs:HList(x, y, width, height)
	local max_item_width = 0
	if self.UniformColumnWidth then
		for _, win in ipairs(self) do
			if not win.Dock then
				max_item_width = Max(max_item_width, win.measure_width)
			end
		end
	end
	local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_width = Max(max_item_width, win.measure_width)
			win:SetLayoutSpace(x, y, item_width, height)
			x = x + item_width + spacing
		end
	end
end


----- VList layout

function XWindowMeasureFuncs:VList(max_width, max_height)
	local item_width, item_height, height_sum, items = 0, 0, 0, 0
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			item_width = Max(item_width, win.measure_width)
			item_height = Max(item_height, win.measure_height)
			height_sum = height_sum + win.measure_height
			items = items + 1
		end
	end
	local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
	return item_width, (self.UniformRowHeight and items * item_height or height_sum) + Max(0, items - 1) * spacing
end

function XWindowLayoutFuncs:VList(x, y, width, height)
	local max_item_height = 0
	if self.UniformRowHeight then
		for _, win in ipairs(self) do
			if not win.Dock then
				max_item_height = Max(max_item_height, win.measure_height)
			end
		end
	end
	local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_height = Max(max_item_height, win.measure_height)
			win:SetLayoutSpace(x, y, width, item_height)
			y = y + item_height + spacing
		end
	end
end


----- HPanel layout

function XWindowMeasureFuncs:HPanel(max_width, max_height)
	local min_width_total_size = 0
	local max_width_total_size = 0
	local total_items = 0
	local sizers = 0
	
	for _, win in ipairs(self) do
		if not win.Dock and not IsKindOf(win, "XPanelSizer") then
			if IsKindOf(win, "XPanelSizer") then
				sizers = sizers + 1
			else
				local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
				min_width_total_size = min_width_total_size + min_width
				max_width_total_size = max_width_total_size + max_width
				total_items = total_items + 1
			end
		end
	end

	if sizers > 0 then
		assert(max_width_total_size >= 100000, "MaxWidths of HPanel's children should be > 1000000")
		if max_width_total_size < 100000 then
			max_width_total_size = 0
			for _, win in ipairs(self) do
				if not win.Dock and not IsKindOf(win, "XPanelSizer") then
					win.MaxWidth = 100000
					local max_width = ScaleXY(win.scale, win.MaxWidth)
					max_width_total_size = max_width_total_size + max_width
				end
			end
		end
	end

	local width_difference_total = max_width_total_size - min_width_total_size
	local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
	local to_distribute = max_width - min_width_total_size - Max(0, total_items - 1) * spacing

	local used_width, height = 0, 0
	for _, win in ipairs(self) do
		if not win.Dock then
			local win_min_width, _, win_max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
			local new_width = win_min_width
			if not IsKindOf(win, "XPanelSizer") then
				local difference = win_max_width - win_min_width
				new_width = win_min_width + Max(0, MulDivRound(to_distribute, difference, width_difference_total))
			end

			win:UpdateMeasure(new_width, max_height)
			height = Max(height, win.measure_height)
			used_width = used_width + win.measure_width
		end
	end
	return used_width + Max(0, total_items - 1) * spacing, height
end

function XWindowLayoutFuncs:HPanel(x, y, width, height)
	local min_width_total_size = 0
	local max_width_total_size = 0
	local total_items = 0
	for _, win in ipairs(self) do
		if not win.Dock then
			local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
			min_width_total_size = min_width_total_size + min_width
			max_width_total_size = max_width_total_size + max_width
			total_items = total_items + 1
		end
	end

	local width_difference_total = max_width_total_size - min_width_total_size
	local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
	local to_distribute = width - min_width_total_size - Max(0, total_items - 1) * spacing

	for _, win in ipairs(self) do
		if not win.Dock then
			local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
			
			local new_width = min_width
			if not IsKindOf(win, "XPanelSizer") then
				local difference = max_width - min_width
				new_width = min_width + Max(0, MulDivRound(to_distribute, difference, width_difference_total))
			end

			win:SetLayoutSpace(x, y, new_width, height)
			x = x + new_width + spacing
		end
	end
end

----- VPanel layout

function XWindowMeasureFuncs:VPanel(max_width, max_height)
	local min_height_total_size = 0
	local max_height_total_size = 0
	local total_items = 0
	local sizers = 0
	
	for _, win in ipairs(self) do
		if not win.Dock then
			if IsKindOf(win, "XPanelSizer") then
				sizers = sizers + 1
			else
				local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
				min_height_total_size = min_height_total_size + min_height
				max_height_total_size = max_height_total_size + max_height
				total_items = total_items + 1
			end
		end
	end
	
	if sizers > 0 then
		assert(max_height_total_size >= 100000, "MaxHeights of VPanel's children should be > 1000000")
		if max_height_total_size < 100000 then
			max_height_total_size = 0
			for _, win in ipairs(self) do
				if not win.Dock and not IsKindOf(win, "XPanelSizer") then
					win.MaxHeight = 100000
					local _, max_height = ScaleXY(win.scale, 0, win.MaxHeight)
					max_height_total_size = max_height_total_size + max_height
				end
			end
		end
	end

	local height_difference_total = max_height_total_size - min_height_total_size
	local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
	local to_distribute = max_height - min_height_total_size - Max(0, total_items - 1) * spacing

	local used_height, width = 0, 0
	for _, win in ipairs(self) do
		if not win.Dock then
			local _, win_min_height, _, win_max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
			local new_height = win_min_height
			if not IsKindOf(win, "XPanelSizer") then
				local difference = win_max_height - win_min_height
				new_height = win_min_height + Max(0, MulDivRound(to_distribute, difference, height_difference_total))
			end

			win:UpdateMeasure(max_width, new_height)
			width = Max(width, win.measure_width)
			used_height = used_height + win.measure_height
		end
	end
	return width, used_height + Max(0, total_items - 1) * spacing
end

function XWindowLayoutFuncs:VPanel(x, y, width, height)
	local min_height_total_size = 0
	local max_height_total_size = 0
	local total_items = 0
	for _, win in ipairs(self) do
		if not win.Dock then
			local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
			min_height_total_size = min_height_total_size + min_height
			max_height_total_size = max_height_total_size + max_height
			total_items = total_items + 1
		end
	end

	local height_difference_total = max_height_total_size - min_height_total_size
	local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
	local to_distribute = height - min_height_total_size - Max(0, total_items - 1) * spacing

	for _, win in ipairs(self) do
		if not win.Dock then
			local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
			
			local new_height = min_height
			if not IsKindOf(win, "XPanelSizer") then
				local difference = max_height - min_height
				new_height = min_height + Max(0, MulDivRound(to_distribute, difference, height_difference_total))
			end

			win:SetLayoutSpace(x, y, width, new_height)
			y = y + new_height + spacing
		end
	end
end

----- Grid layout

function XWindowMeasureFuncs:Grid(max_width, max_height)
	local width, height = 0, 0
	local max_col, max_row = 0, 0
	local col_width, row_height
	local col_widths, row_heights
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			max_col = Max(max_col, win.GridX + win.GridWidth - 1)
			max_row = Max(max_row, win.GridY + win.GridHeight - 1)
			local width_per_col = win.measure_width / win.GridWidth
			local height_per_row = win.measure_height / win.GridHeight
			if self.UniformColumnWidth then
				col_width = Max(col_width, width_per_col)
			else
				col_widths = col_widths or {}
				for i = win.GridX, win.GridX + win.GridWidth - 1 do
					col_widths[i] = Max(col_widths[i], width_per_col)
				end
			end
			if self.UniformRowHeight then
				row_height = Max(row_height, height_per_row)
			else
				row_heights = row_heights or {}
				for i = win.GridY, win.GridY + win.GridHeight - 1 do
					row_heights[i] = Max(row_heights[i], height_per_row)
				end
			end
		end
	end
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	for i = 1, max_col do
		width = width + (col_width or col_widths[i] or 0)
	end
	width = width + Max(0, max_col - 1) * h_spacing
	for i = 1, max_row do
		height = height + (row_height or row_heights[i] or 0)
	end
	height = height + Max(0, max_row - 1) * v_spacing
	return width, height
end

function XWindowLayoutFuncs:Grid(x, y, width, height)
	local col_width, row_height
	local col_widths, row_heights = {}, {}
	local num_cols, num_rows
	for _, win in ipairs(self) do
		if not win.Dock then
			local width_per_col = win.measure_width / win.GridWidth
			local height_per_row = win.measure_height / win.GridHeight
			if self.UniformColumnWidth then
				col_width = Max(col_width, width_per_col)
			else
				for i = win.GridX, win.GridX + win.GridWidth - 1 do
					col_widths[i] = Max(col_widths[i], width_per_col)
				end
			end
			num_cols = Max(num_cols, win.GridX + win.GridWidth - 1)
			if self.UniformRowHeight then
				row_height = Max(row_height, height_per_row)
			else
				for i = win.GridY, win.GridY + win.GridHeight - 1 do
					row_heights[i] = Max(row_heights[i], height_per_row)
				end
			end
			num_rows = Max(num_rows, win.GridY + win.GridHeight - 1)
		end
	end
	local total_width, total_height = 0, 0
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	if self.UniformColumnWidth then
		total_width = col_width * num_cols
	else
		for i = 1, num_cols do
			total_width = total_width + (col_widths[i] or 0)
		end
	end
	
	local h_spacing_sum = Max(0, num_cols - 1) * h_spacing
	local total_width_noSpacing = total_width
	local width_noSpacing = width - h_spacing_sum
	total_width = total_width + h_spacing_sum
	
	if self.UniformRowHeight then
		total_height = row_height * num_rows
	else
		for i = 1, num_rows do
			total_height = total_height + (row_heights[i] or 0)
		end
	end
	
	local v_spacing_sum = Max(0, num_rows - 1) * v_spacing
	local total_height_noSpacing = total_height
	local height_noSpacing = height - v_spacing_sum
	total_height = total_height + v_spacing_sum
	
	for _, win in ipairs(self) do
		if not win.Dock then
			local x_left = x
			local space_width = 0
			if total_width_noSpacing > 0 then
				for i = 1, win.GridX - 1 do
					-- GridStretch(X/Y) means we want to distribute the available space to the grid's children while
					-- maintaining their ratio relative to each other (their space relative to the total measured space).
					-- This can cause the items to either shrink or stretch.
					-- If it is set to false then children will be given their measure size and could spill.
					if self.GridStretchX then
						x_left = x_left + (col_width or col_widths[i] or 0) * width_noSpacing / total_width_noSpacing
					else
						x_left = x_left + (col_width or col_widths[i] or 0)
					end
				end
				x_left = x_left + Max(0, (win.GridX - 1)) * h_spacing
				for i = win.GridX, win.GridX + win.GridWidth - 1 do
					if self.GridStretchX then
						space_width = space_width + (col_width or col_widths[i] or 0) * width_noSpacing / total_width_noSpacing
					else
						space_width = space_width + (col_width or col_widths[i] or 0)
					end
				end
				space_width = space_width + Max(0, (win.GridWidth - 1)) * h_spacing
			end
			local y_top = y
			local space_height = 0
			if total_height_noSpacing > 0 then
				for i = 1, win.GridY - 1 do
					if self.GridStretchY then
						y_top = y_top + (row_height or row_heights[i] or 0) * height_noSpacing / total_height_noSpacing
					else
						y_top = y_top + (row_height or row_heights[i] or 0)
					end
				end
				y_top = y_top + Max(0, (win.GridY - 1)) * v_spacing
				for i = win.GridY, win.GridY + win.GridHeight - 1 do
					if self.GridStretchY then
						space_height = space_height + (row_height or row_heights[i] or 0) * height_noSpacing / total_height_noSpacing
					else
						space_height = space_height + (row_height or row_heights[i] or 0)
					end
				end
				space_height = space_height + Max(0, (win.GridHeight - 1)) * v_spacing
			end
			win:SetLayoutSpace(x_left, y_top, space_width, space_height)
		end
	end
end

----- HWrap layout

function XWindowMeasureFuncs:HWrap(max_width, max_height)
	local line_width, line_height, total_width, total_height = 0, 0, 0, 0
	local max_item_width, max_item_height, items = 0, 0, 0
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
			max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
		end
	end
	
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_width = Max(max_item_width, win.measure_width)
			local item_height = Max(max_item_height, win.measure_height)
			local new_width = line_width + (line_width > 0 and h_spacing or 0) + item_width
			if new_width > max_width then
				total_width = Max(total_width, line_width)
				total_height = total_height + (total_height > 0 and v_spacing or 0) + line_height
				line_width = item_width
				line_height = item_height
			else
				line_width = new_width
				line_height = Max(line_height, item_height)
			end
			items = items + 1
		end
	end
	total_width = Max(total_width, line_width)
	total_height = total_height + (total_height > 0 and v_spacing or 0) + line_height
	return total_width, total_height
end

function XWindowLayoutFuncs:HWrap(x, y, width, height)
	local x_left = x
	local max_item_width, max_item_height = 0, 0
	if self.UniformColumnWidth or self.UniformRowHeight then
		for _, win in ipairs(self) do
			if not win.Dock then
				max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
				max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
			end
		end
	end
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	local line_height = 0
	for i = 1, #self do
		local win = self[i]
		if not win.Dock then
			local item_width = Max(max_item_width, win.measure_width)
			local item_height = Max(max_item_height, win.measure_height)
			local x_right = x + item_width
			if x_right > x_left + width then
				local x_offset = x_left
				if self.HAlign == "center" then
					--center items on every line
					local line_items_width = 0
					for j = i, #self do
						local line_win = self[j]
						if line_items_width > width then
							--can't fit more items on this row
							break
						end
						line_items_width = line_items_width + Max(max_item_width, line_win.measure_width)
					end
					if line_items_width < width then
						x_offset = x_left + (width - line_items_width) / 2
					end
				end
				x = x_offset
				y = y + line_height + (line_height > 0 and v_spacing or 0)
				line_height = 0
			end
			win:SetLayoutSpace(x, y, item_width, item_height)
			line_height = Max(line_height, item_height)
			x = x + item_width + h_spacing
		end
	end
end

----- VWrap layout

function XWindowMeasureFuncs:VWrap(max_width, max_height)
	local col_width, col_height, total_width, total_height = 0, 0, 0, 0
	local max_item_width, max_item_height, items = 0, 0, 0
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	
	for _, win in ipairs(self) do
		if not win.Dock then
			win:UpdateMeasure(max_width, max_height)
			max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
			max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
		end
	end
	
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_width = Max(max_item_width, win.measure_width)
			local item_height = Max(max_item_height, win.measure_height)
			local new_height = col_height + (col_height > 0 and v_spacing or 0) + item_height
			if new_height > max_height then
				total_height = Max(total_height, col_height)
				total_width = total_width + (total_width > 0 and h_spacing or 0) + col_width
				col_width = item_width
				col_height = item_height
			else
				col_height = new_height
				col_width = Max(col_width, item_width)
			end
			items = items + 1
		end
	end
	total_height = Max(total_height, col_height)
	total_width = total_width + (total_width > 0 and h_spacing or 0) + col_width
	return total_width, total_height
end

function XWindowLayoutFuncs:VWrap(x, y, width, height)
	local y_top = y
	local max_item_width, max_item_height = 0, 0
	if self.UniformColumnWidth or self.UniformRowHeight then
		for _, win in ipairs(self) do
			if not win.Dock then
				max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
				max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
			end
		end
	end
	local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
	local line_width = 0
	for _, win in ipairs(self) do
		if not win.Dock then
			local item_width = Max(max_item_width, win.measure_width)
			local item_height = Max(max_item_height, win.measure_height)
			local y_bottom = y + item_height
			if y_bottom > y_top + height then
				y = y_top
				x = x + line_width + (line_width > 0 and h_spacing or 0)
				line_width = 0
			end
			win:SetLayoutSpace(x, y, item_width, item_height)
			line_width = Max(line_width, item_width)
			y = y + item_height + v_spacing
		end
	end
end