all: initial import

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-03-30 13:18:49 +01:00
commit 0f05231c35
102 changed files with 17926 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: Unlicense OR MIT
package layout
import (
"image"
"gioui.org/ui/f32"
"gioui.org/ui"
)
type Flex struct {
Axis Axis
MainAxisAlignment MainAxisAlignment
CrossAxisAlignment CrossAxisAlignment
MainAxisSize MainAxisSize
cs Constraints
children []flexChild
taken int
maxCross int
maxBaseline int
ccache [10]flexChild
opCache [10]ui.Op
}
type flexChild struct {
op ui.Op
dims Dimens
}
type MainAxisSize uint8
type FlexMode uint8
type MainAxisAlignment uint8
type CrossAxisAlignment uint8
const (
Loose FlexMode = iota
Fit
)
const (
Max MainAxisSize = iota
Min
)
const (
Start = 100 + iota
End
Center
SpaceAround MainAxisAlignment = iota
SpaceBetween
SpaceEvenly
Baseline CrossAxisAlignment = iota
Stretch
)
func (f *Flex) Init(cs Constraints) *Flex {
f.cs = cs
if f.children == nil {
f.children = f.ccache[:0]
}
f.children = f.children[:0]
f.maxCross = 0
f.maxBaseline = 0
return f
}
func (f *Flex) Rigid(w Widget) *Flex {
mainc := axisMainConstraint(f.Axis, f.cs)
mainMax := mainc.Max
if mainc.Max != ui.Inf {
mainMax -= f.taken
}
cs := axisConstraints(f.Axis, Constraint{Max: mainMax}, f.crossConstraintChild(f.cs))
op, dims := w.Layout(cs)
f.taken += axisMain(f.Axis, dims.Size)
if c := axisCross(f.Axis, dims.Size); c > f.maxCross {
f.maxCross = c
}
if b := dims.Baseline; b > f.maxBaseline {
f.maxBaseline = b
}
f.children = append(f.children, flexChild{op, dims})
return f
}
func (f *Flex) Flexible(idx int, flex float32, mode FlexMode, w Widget) *Flex {
mainc := axisMainConstraint(f.Axis, f.cs)
var flexSize int
if mainc.Max != ui.Inf && mainc.Max > f.taken {
flexSize = mainc.Max - f.taken
}
submainc := Constraint{Max: int(float32(flexSize) * flex)}
if mode == Fit {
submainc.Min = submainc.Max
}
cs := axisConstraints(f.Axis, submainc, f.crossConstraintChild(f.cs))
op, dims := w.Layout(cs)
f.taken += axisMain(f.Axis, dims.Size)
if c := axisCross(f.Axis, dims.Size); c > f.maxCross {
f.maxCross = c
}
if b := dims.Baseline; b > f.maxBaseline {
f.maxBaseline = b
}
f.children = append(f.children, flexChild{op, dims})
if idx < 0 {
idx += len(f.children)
}
f.children[idx], f.children[len(f.children)-1] = f.children[len(f.children)-1], f.children[idx]
return f
}
func (f *Flex) Layout() (ui.Op, Dimens) {
mainc := axisMainConstraint(f.Axis, f.cs)
crossSize := axisCrossConstraint(f.Axis, f.cs).Constrain(f.maxCross)
var space int
if mainc.Max != ui.Inf && f.MainAxisSize == Max {
if mainc.Max > f.taken {
space = mainc.Max - f.taken
}
} else if mainc.Min > f.taken {
space = mainc.Min - f.taken
}
var mainSize int
var baseline int
switch f.MainAxisAlignment {
case Center:
mainSize += space / 2
case End:
mainSize += space
case SpaceEvenly:
mainSize += space / (1 + len(f.children))
case SpaceAround:
mainSize += space / (len(f.children) * 2)
}
var ops ui.Ops
if len(f.children) > len(f.opCache) {
ops = make([]ui.Op, len(f.children))
} else {
ops = f.opCache[:len(f.children)]
}
for i, child := range f.children {
dims := child.dims
b := dims.Baseline
var cross int
switch f.CrossAxisAlignment {
case End:
cross = crossSize - axisCross(f.Axis, dims.Size)
case Center:
cross = (crossSize - axisCross(f.Axis, dims.Size)) / 2
case Baseline:
if f.Axis == Horizontal {
cross = f.maxBaseline - b
}
}
off := ui.Offset(toPointF(axisPoint(f.Axis, mainSize, cross)))
ops[i] = ui.OpLayer{Op: ui.OpTransform{Transform: off, Op: child.op}}
mainSize += axisMain(f.Axis, dims.Size)
switch f.MainAxisAlignment {
case SpaceEvenly:
mainSize += space / (1 + len(f.children))
case SpaceAround:
mainSize += space / len(f.children)
case SpaceBetween:
mainSize += space / (len(f.children) - 1)
}
if b != dims.Size.Y {
baseline = b
}
}
switch f.MainAxisAlignment {
case Start:
mainSize += space
case SpaceEvenly:
mainSize += space / (1 + len(f.children))
case SpaceAround:
mainSize += space / (len(f.children) * 2)
}
sz := axisPoint(f.Axis, mainSize, crossSize)
if baseline == 0 {
baseline = sz.Y
}
return ops, Dimens{Size: sz, Baseline: baseline}
}
func axisPoint(a Axis, main, cross int) image.Point {
if a == Horizontal {
return image.Point{main, cross}
} else {
return image.Point{cross, main}
}
}
func axisMain(a Axis, sz image.Point) int {
if a == Horizontal {
return sz.X
} else {
return sz.Y
}
}
func axisCross(a Axis, sz image.Point) int {
if a == Horizontal {
return sz.Y
} else {
return sz.X
}
}
func axisMainConstraint(a Axis, cs Constraints) Constraint {
if a == Horizontal {
return cs.Width
} else {
return cs.Height
}
}
func axisCrossConstraint(a Axis, cs Constraints) Constraint {
if a == Horizontal {
return cs.Height
} else {
return cs.Width
}
}
func (f *Flex) crossConstraintChild(cs Constraints) Constraint {
c := axisCrossConstraint(f.Axis, cs)
switch f.CrossAxisAlignment {
case Stretch:
c.Min = c.Max
default:
c.Min = 0
}
return c
}
func axisConstraints(a Axis, mainc, crossc Constraint) Constraints {
if a == Horizontal {
return Constraints{mainc, crossc}
} else {
return Constraints{crossc, mainc}
}
}
func toPointF(p image.Point) f32.Point {
return f32.Point{X: float32(p.X), Y: float32(p.Y)}
}
+206
View File
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: Unlicense OR MIT
package layout
import (
"image"
"gioui.org/ui/draw"
"gioui.org/ui/gesture"
"gioui.org/ui/pointer"
"gioui.org/ui"
)
type scrollChild struct {
op ui.Op
size image.Point
}
type List struct {
Axis Axis
CrossAxisAlignment CrossAxisAlignment
// The distance scrolled since last call to Init.
Distance int
scroll gesture.Scroll
scrollDir int
offset int
first int
cs Constraints
len int
maxSize int
children []scrollChild
elem func(w Widget)
size image.Point
ops ui.Ops
}
type Interface interface {
Len() int
At(i int) Widget
}
func (l *List) Init(cs Constraints, len int) (int, bool) {
l.maxSize = 0
l.children = l.children[:0]
l.cs = cs
l.len = len
l.elem = nil
if l.first > len {
l.first = len
}
l.ops = l.ops[:0]
if len == 0 {
return 0, false
}
return l.Index()
}
func (l *List) Dragging() bool {
return l.scroll.Dragging()
}
func (l *List) Scroll(c *ui.Config, q pointer.Events) {
l.Distance = 0
d := l.scroll.Scroll(c, q, gesture.Axis(l.Axis))
l.scrollDir = d
l.Distance += d
l.offset += d
}
func (l *List) Index() (int, bool) {
i, ok := l.next()
if !ok {
l.draw()
}
return i, ok
}
func (l *List) Layout() (ui.Op, Dimens) {
ops := append(ui.Ops{l.scroll.Op(gesture.Rect(l.size))}, l.ops...)
return ops, Dimens{Size: l.size}
}
func (l *List) next() (int, bool) {
mainc := axisMainConstraint(l.Axis, l.cs)
if l.offset <= 0 {
if l.first > 0 {
l.elem = l.backward
return l.first - 1, true
}
l.offset = 0
}
if l.maxSize-l.offset < mainc.Max {
i := l.first + len(l.children)
if i < l.len {
l.elem = l.forward
return i, true
}
missing := mainc.Max - (l.maxSize - l.offset)
if missing > l.offset {
missing = l.offset
}
l.offset -= missing
}
return 0, false
}
func (l *List) Elem(w Widget) {
l.elem(w)
}
func (l *List) backward(w Widget) {
subcs := axisConstraints(l.Axis, Constraint{Max: ui.Inf}, l.crossConstraintChild(l.cs))
l.first--
op, dims := w.Layout(subcs)
mainSize := axisMain(l.Axis, dims.Size)
l.offset += mainSize
l.maxSize += mainSize
l.children = append([]scrollChild{{op, dims.Size}}, l.children...)
}
func (l *List) forward(w Widget) {
subcs := axisConstraints(l.Axis, Constraint{Max: ui.Inf}, l.crossConstraintChild(l.cs))
op, dims := w.Layout(subcs)
mainSize := axisMain(l.Axis, dims.Size)
l.maxSize += mainSize
l.children = append(l.children, scrollChild{op, dims.Size})
}
func (l *List) draw() {
mainc := axisMainConstraint(l.Axis, l.cs)
for len(l.children) > 0 {
sz := l.children[0].size
mainSize := axisMain(l.Axis, sz)
if l.offset <= mainSize {
break
}
l.first++
l.offset -= mainSize
l.children = l.children[1:]
}
size := -l.offset
var maxCross int
for i, child := range l.children {
sz := child.size
if c := axisCross(l.Axis, sz); c > maxCross {
maxCross = c
}
size += axisMain(l.Axis, sz)
if size >= mainc.Max {
l.children = l.children[:i+1]
break
}
}
pos := -l.offset
for _, child := range l.children {
sz := child.size
var cross int
switch l.CrossAxisAlignment {
case End:
cross = maxCross - axisCross(l.Axis, sz)
case Center:
cross = (maxCross - axisCross(l.Axis, sz)) / 2
}
max := axisMain(l.Axis, sz) + pos
if max > mainc.Max {
max = mainc.Max
}
min := pos
if min < 0 {
min = 0
}
op := draw.ClipRect(
image.Rectangle{
Min: axisPoint(l.Axis, min, -ui.Inf),
Max: axisPoint(l.Axis, max, ui.Inf),
},
ui.OpTransform{Transform: ui.Offset(toPointF(axisPoint(l.Axis, pos, cross))), Op: child.op},
)
l.ops = append(l.ops, ui.OpLayer{Op: op})
pos += axisMain(l.Axis, sz)
}
atStart := l.first == 0 && l.offset <= 0
atEnd := l.first+len(l.children) == l.len && mainc.Max >= pos
if atStart && l.scrollDir < 0 || atEnd && l.scrollDir > 0 {
l.scroll.Stop()
}
l.size = axisPoint(l.Axis, mainc.Constrain(pos), maxCross)
}
func (l *List) crossConstraintChild(cs Constraints) Constraint {
c := axisCrossConstraint(l.Axis, cs)
switch l.CrossAxisAlignment {
case Stretch:
c.Min = c.Max
default:
c.Min = 0
}
return c
}
+213
View File
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: Unlicense OR MIT
package layout
import (
"image"
"math"
"gioui.org/ui"
)
type Widget interface {
Layout(cs Constraints) (ui.Op, Dimens)
}
type Constraints struct {
Width Constraint
Height Constraint
}
type Constraint struct {
Min, Max int
}
type Dimens struct {
Size image.Point
Baseline int
}
type Axis uint8
type F func(cs Constraints) (ui.Op, Dimens)
const (
Horizontal Axis = iota
Vertical
)
func (c Constraint) Constrain(v int) int {
if v < c.Min {
return c.Min
} else if v > c.Max {
return c.Max
}
return v
}
func (c Constraints) Constrain(p image.Point) image.Point {
return image.Point{X: c.Width.Constrain(p.X), Y: c.Height.Constrain(p.Y)}
}
func (c Constraints) Expand() Constraints {
return Constraints{Width: c.Width.Expand(), Height: c.Height.Expand()}
}
func (c Constraint) Expand() Constraint {
return Constraint{Min: c.Max, Max: c.Max}
}
func (c Constraints) Loose() Constraints {
return Constraints{Width: c.Width.Loose(), Height: c.Height.Loose()}
}
func (c Constraint) Loose() Constraint {
return Constraint{Max: c.Max}
}
// ExactConstraints returns the constraints that exactly represents the
// given dimensions.
func ExactConstraints(size image.Point) Constraints {
return Constraints{
Width: Constraint{Min: size.X, Max: size.X},
Height: Constraint{Min: size.Y, Max: size.Y},
}
}
func (f F) Layout(cs Constraints) (ui.Op, Dimens) {
return f(cs)
}
type Margins struct {
Top, Right, Bottom, Left ui.Value
}
func Margin(c *ui.Config, m Margins, w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
mcs := cs
t, r, b, l := int(c.Pixels(m.Top)+0.5), int(c.Pixels(m.Right)+0.5), int(c.Pixels(m.Bottom)+0.5), int(c.Pixels(m.Left)+0.5)
if mcs.Width.Max != ui.Inf {
mcs.Width.Min -= l + r
mcs.Width.Max -= l + r
if mcs.Width.Min < 0 {
mcs.Width.Min = 0
}
if mcs.Width.Max < mcs.Width.Min {
mcs.Width.Max = mcs.Width.Min
}
}
if mcs.Height.Max != ui.Inf {
mcs.Height.Min -= t + b
mcs.Height.Max -= t + b
if mcs.Height.Min < 0 {
mcs.Height.Min = 0
}
if mcs.Height.Max < mcs.Height.Min {
mcs.Height.Max = mcs.Height.Min
}
}
op, dims := w.Layout(mcs)
op = ui.OpTransform{Transform: ui.Offset(toPointF(image.Point{X: l, Y: t})), Op: op}
return op, Dimens{
Size: cs.Constrain(dims.Size.Add(image.Point{X: r + l, Y: t + b})),
Baseline: dims.Baseline + t,
}
})
}
func EqualMargins(v ui.Value) Margins {
return Margins{Top: v, Right: v, Bottom: v, Left: v}
}
func isInf(v ui.Value) bool {
return math.IsInf(float64(v.V), 1)
}
func Capped(c *ui.Config, maxWidth, maxHeight ui.Value, wt Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
if !isInf(maxWidth) {
mw := int(c.Pixels(maxWidth) + .5)
if mw < cs.Width.Min {
mw = cs.Width.Min
}
if mw < cs.Width.Max {
cs.Width.Max = mw
}
}
if !isInf(maxHeight) {
mh := int(c.Pixels(maxHeight) + 0.5)
if mh < cs.Height.Min {
mh = cs.Height.Min
}
if mh < cs.Height.Max {
cs.Height.Max = mh
}
}
return wt.Layout(cs)
})
}
func Sized(c *ui.Config, width, height ui.Value, wt Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
if h := int(c.Pixels(height) + 0.5); h != 0 {
if cs.Height.Min < h {
cs.Height.Min = h
}
if h < cs.Height.Max {
cs.Height.Max = h
}
}
if w := int(c.Pixels(width) + .5); w != 0 {
if cs.Width.Min < w {
cs.Width.Min = w
}
if w < cs.Width.Max {
cs.Width.Max = w
}
}
return wt.Layout(cs)
})
}
func Expand(w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
if cs.Height.Max != ui.Inf {
cs.Height.Min = cs.Height.Max
}
if cs.Width.Max != ui.Inf {
cs.Width.Min = cs.Width.Max
}
return w.Layout(cs)
})
}
func Align(alignment Direction, w Widget) Widget {
return F(func(cs Constraints) (ui.Op, Dimens) {
op, dims := w.Layout(cs.Loose())
sz := dims.Size
if cs.Width.Max != ui.Inf {
sz.X = cs.Width.Max
}
if cs.Height.Max != ui.Inf {
sz.Y = cs.Height.Max
}
var p image.Point
switch alignment {
case N, S, Center:
p.X = (sz.X - dims.Size.X) / 2
case NE, SE, E:
p.X = sz.X - dims.Size.X
}
switch alignment {
case W, Center, E:
p.Y = (sz.Y - dims.Size.Y) / 2
case SW, S, SE:
p.Y = sz.Y - dims.Size.Y
}
op = ui.OpTransform{Transform: ui.Offset(toPointF(p)), Op: op}
return op, Dimens{
Size: sz,
Baseline: dims.Baseline,
}
})
}
+118
View File
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: Unlicense OR MIT
package layout
import (
"image"
"gioui.org/ui"
)
type Stack struct {
Alignment Direction
cs Constraints
children []stackChild
maxSZ image.Point
baseline int
ccache [10]stackChild
opCache [10]ui.Op
}
type stackChild struct {
op ui.Op
dims Dimens
}
type Direction uint8
const (
NW Direction = iota
N
NE
E
SE
S
SW
W
)
func (s *Stack) Init(cs Constraints) *Stack {
if s.children == nil {
s.children = s.ccache[:0]
}
s.children = s.children[:0]
s.maxSZ = image.Point{}
s.baseline = 0
s.cs = cs
return s
}
func (s *Stack) Rigid(w Widget) *Stack {
op, dims := w.Layout(s.cs)
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
}
s.add(op, dims)
return s
}
func (s *Stack) Expand(idx int, w Widget) *Stack {
cs := Constraints{
Width: Constraint{Min: s.maxSZ.X, Max: s.maxSZ.X},
Height: Constraint{Min: s.maxSZ.Y, Max: s.maxSZ.Y},
}
s.add(w.Layout(cs))
if idx < 0 {
idx += len(s.children)
}
s.children[idx], s.children[len(s.children)-1] = s.children[len(s.children)-1], s.children[idx]
return s
}
func (s *Stack) add(op ui.Op, dims Dimens) {
s.children = append(s.children, stackChild{op, dims})
if s.baseline == 0 {
if b := dims.Baseline; b != dims.Size.Y {
s.baseline = b
}
}
}
func (s *Stack) Layout() (ui.Op, Dimens) {
var ops ui.Ops
if len(s.children) > len(s.opCache) {
ops = make([]ui.Op, len(s.children))
} else {
ops = s.opCache[:len(s.children)]
}
for i, ch := range s.children {
sz := ch.dims.Size
var p image.Point
switch s.Alignment {
case N, S, Center:
p.X = (s.maxSZ.X - sz.X) / 2
case NE, SE, E:
p.X = s.maxSZ.X - sz.X
}
switch s.Alignment {
case W, Center, E:
p.Y = (s.maxSZ.Y - sz.Y) / 2
case SW, S, SE:
p.Y = s.maxSZ.Y - sz.Y
}
ops[i] = ui.OpLayer{Op: ui.OpTransform{Transform: ui.Offset(toPointF(p)), Op: ch.op}}
}
b := s.baseline
if b == 0 {
b = s.maxSZ.Y
}
return ops, Dimens{
Size: s.maxSZ,
Baseline: b,
}
}