forked from joejulian/gio
3af01a3f43
Change the definition of Widget from the implicit
type Widget func()
to the explicit functional
type Widget func(gtx layout.Context) layout.Dimensions
The advantages are numerous:
- Clearer connection between the incoming context and the output dimensions.
- Returning the Dimensions are impossible to omit.
- Contexts passed by value, so its fields can be exported
and freely mutated by the program.
The only disadvantage is the longer function literals and the many "returns".
What tipped the scales in favour of the explicit Widget variant is that type
aliases can dramatically shorten the literals:
type (
C = layout.Context
D = layout.Dimensions
)
widget := func(gtx C) D {
...
}
Note that the aliases are not part of the Gio API and it is up to each user
whether they want to use them.
Finally the Go proposal for lightweight function literals,
https://github.com/golang/go/issues/21498, may remove the disadvantage
completely in future.
Context becomes a plain struct with only public fields, and its Reset is
replaced by a NewContext convenience constructor.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
128 lines
2.7 KiB
Go
128 lines
2.7 KiB
Go
// 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
|
|
}
|
|
|
|
// StackChild represents a child for a Stack layout.
|
|
type StackChild struct {
|
|
expanded bool
|
|
widget Widget
|
|
|
|
// Scratch space.
|
|
macro op.MacroOp
|
|
dims Dimensions
|
|
}
|
|
|
|
// Stacked returns a Stack child that is laid out with no minimum
|
|
// constraints and the maximum constraints passed to Stack.Layout.
|
|
func Stacked(w Widget) StackChild {
|
|
return StackChild{
|
|
widget: w,
|
|
}
|
|
}
|
|
|
|
// Expanded returns a Stack child with the minimum constraints set
|
|
// to the largest Stacked child. The maximum constraints are set to
|
|
// the same as passed to Stack.Layout.
|
|
func Expanded(w Widget) StackChild {
|
|
return StackChild{
|
|
expanded: true,
|
|
widget: w,
|
|
}
|
|
}
|
|
|
|
// Layout a stack of children. The position of the children are
|
|
// determined by the specified order, but Stacked children are laid out
|
|
// before Expanded children.
|
|
func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
|
|
var maxSZ image.Point
|
|
// First lay out Stacked children.
|
|
for i, w := range children {
|
|
if w.expanded {
|
|
continue
|
|
}
|
|
var m op.MacroOp
|
|
m.Record(gtx.Ops)
|
|
gtx := gtx
|
|
gtx.Constraints.Min = image.Pt(0, 0)
|
|
dims := w.widget(gtx)
|
|
m.Stop()
|
|
if w := dims.Size.X; w > maxSZ.X {
|
|
maxSZ.X = w
|
|
}
|
|
if h := dims.Size.Y; h > maxSZ.Y {
|
|
maxSZ.Y = h
|
|
}
|
|
children[i].macro = m
|
|
children[i].dims = dims
|
|
}
|
|
// Then lay out Expanded children.
|
|
for i, w := range children {
|
|
if !w.expanded {
|
|
continue
|
|
}
|
|
var m op.MacroOp
|
|
m.Record(gtx.Ops)
|
|
gtx := gtx
|
|
gtx.Constraints = Constraints{
|
|
Min: maxSZ, Max: gtx.Constraints.Max,
|
|
}
|
|
dims := w.widget(gtx)
|
|
m.Stop()
|
|
if w := dims.Size.X; w > maxSZ.X {
|
|
maxSZ.X = w
|
|
}
|
|
if h := dims.Size.Y; h > maxSZ.Y {
|
|
maxSZ.Y = h
|
|
}
|
|
children[i].macro = m
|
|
children[i].dims = dims
|
|
}
|
|
|
|
maxSZ = gtx.Constraints.Constrain(maxSZ)
|
|
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(FPt(p)).Add(gtx.Ops)
|
|
ch.macro.Add()
|
|
stack.Pop()
|
|
if baseline == 0 {
|
|
if b := ch.dims.Baseline; b != 0 {
|
|
baseline = b + maxSZ.Y - sz.Y - p.Y
|
|
}
|
|
}
|
|
}
|
|
return Dimensions{
|
|
Size: maxSZ,
|
|
Baseline: baseline,
|
|
}
|
|
}
|