gio/layout/stack.go -rw-r--r-- 2.4 KiB View raw
                                                                                
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
// SPDX-License-Identifier: Unlicense OR MIT

package layout

import (
	"image"

	"gioui.org/op"
)

// Stack lays out child elements on top of each other,
// according to an alignment direction.
type Stack struct {
	// Alignment is the direction to align children
	// smaller than the available space.
	Alignment Direction

	maxSZ image.Point
	// Use an empty StackOp for tracking whether Rigid, Flex
	// is called in the same layout scope as Layout.
	begun bool
	stack op.StackOp
}

// StackChild is the layout result of a call to End.
type StackChild struct {
	macro op.MacroOp
	dims  Dimensions
}

// Rigid lays out a widget with the same constraints that were
// passed to Init.
func (s *Stack) Rigid(gtx *Context, w Widget) StackChild {
	cs := gtx.Constraints
	cs.Width.Min = 0
	cs.Height.Min = 0
	var m op.MacroOp
	m.Record(gtx.Ops)
	dims := ctxLayout(gtx, cs, w)
	m.Stop()
	s.expand(gtx.Ops, dims)
	return StackChild{m, dims}
}

// Expand lays out a widget.
func (s *Stack) Expand(gtx *Context, w Widget) StackChild {
	var m op.MacroOp
	m.Record(gtx.Ops)
	cs := Constraints{
		Width:  Constraint{Min: s.maxSZ.X, Max: gtx.Constraints.Width.Max},
		Height: Constraint{Min: s.maxSZ.Y, Max: gtx.Constraints.Height.Max},
	}
	dims := ctxLayout(gtx, cs, w)
	m.Stop()
	s.expand(gtx.Ops, dims)
	return StackChild{m, dims}
}

func (s *Stack) expand(ops *op.Ops, dims Dimensions) {
	if !s.begun {
		s.stack.Push(ops)
		s.begun = true
	}
	if w := dims.Size.X; w > s.maxSZ.X {
		s.maxSZ.X = w
	}
	if h := dims.Size.Y; h > s.maxSZ.Y {
		s.maxSZ.Y = h
	}
}

// Layout a list of children. The order of the children determines their laid
// out order.
func (s *Stack) Layout(gtx *Context, children ...StackChild) {
	if len(children) > 0 {
		s.stack.Pop()
	}
	maxSZ := gtx.Constraints.Constrain(s.maxSZ)
	s.maxSZ = image.Point{}
	s.begun = false
	var baseline int
	for _, ch := range children {
		sz := ch.dims.Size
		var p image.Point
		switch s.Alignment {
		case N, S, Center:
			p.X = (maxSZ.X - sz.X) / 2
		case NE, SE, E:
			p.X = maxSZ.X - sz.X
		}
		switch s.Alignment {
		case W, Center, E:
			p.Y = (maxSZ.Y - sz.Y) / 2
		case SW, S, SE:
			p.Y = maxSZ.Y - sz.Y
		}
		var stack op.StackOp
		stack.Push(gtx.Ops)
		op.TransformOp{}.Offset(toPointF(p)).Add(gtx.Ops)
		ch.macro.Add(gtx.Ops)
		stack.Pop()
		if baseline == 0 {
			if b := ch.dims.Baseline; b != 0 {
				baseline = b + maxSZ.Y - sz.Y - p.Y
			}
		}
	}
	gtx.Dimensions = Dimensions{
		Size:     maxSZ,
		Baseline: baseline,
	}
}