blob: 47f940f32eafa9c82aa67e5849592f5571f1fbf4 [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flex
import (
"bytes"
"fmt"
"image"
"image/color"
"testing"
"golang.org/x/exp/shiny/unit"
"golang.org/x/exp/shiny/widget"
"golang.org/x/exp/shiny/widget/node"
)
type layoutTest struct {
direction Direction
wrap FlexWrap
alignContent AlignContent
size image.Point // size of container
measured [][2]float64 // MeasuredSize of child elements
layoutData []LayoutData // LayoutData of child elements
want []image.Rectangle // final Rect of child elements
}
func (t *layoutTest) html() string {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, `<style>
#container {
display: flex;
width: %dpx;
height: %dpx;
`, t.size.X, t.size.Y)
switch t.direction {
case Row:
case RowReverse:
fmt.Fprintf(buf, "\tflex-direction: row-reverse;\n")
case Column:
fmt.Fprintf(buf, "\tflex-direction: column;\n")
case ColumnReverse:
fmt.Fprintf(buf, "\tflex-direction: column-reverse;\n")
}
switch t.wrap {
case NoWrap:
case Wrap:
fmt.Fprintf(buf, "\tflex-wrap: wrap;\n")
case WrapReverse:
fmt.Fprintf(buf, "\tflex-wrap: wrap-reverse;\n")
}
switch t.alignContent {
case AlignContentStart:
case AlignContentEnd:
fmt.Fprintf(buf, "\talign-content: flex-end;\n")
case AlignContentCenter:
fmt.Fprintf(buf, "\talign-content: center;\n")
case AlignContentSpaceBetween:
fmt.Fprintf(buf, "\talign-content: space-between;\n")
case AlignContentSpaceAround:
fmt.Fprintf(buf, "\talign-content: space-around;\n")
case AlignContentStretch:
fmt.Fprintf(buf, "\talign-content: stretch;\n")
}
fmt.Fprintf(buf, "}\n")
for i, m := range t.measured {
fmt.Fprintf(buf, `#child%d {
width: %.2fpx;
height: %.2fpx;
`, i, m[0], m[1])
c := colors[i%len(colors)]
fmt.Fprintf(buf, "\tbackground-color: rgb(%d, %d, %d);\n", c.R, c.G, c.B)
if t.layoutData != nil {
d := t.layoutData[i]
if d.MinSize.X != 0 {
fmt.Fprintf(buf, "\tmin-width: %dpx;\n", d.MinSize.X)
}
if d.MinSize.Y != 0 {
fmt.Fprintf(buf, "\tmin-height: %dpx;\n", d.MinSize.Y)
}
if d.MaxSize != nil {
fmt.Fprintf(buf, "\tmax-width: %dpx;\n", d.MaxSize.X)
fmt.Fprintf(buf, "\tmax-height: %dpx;\n", d.MaxSize.Y)
}
if d.Grow != 0 {
fmt.Fprintf(buf, "\tflex-grow: %f;\n", d.Grow)
}
if d.Shrink != nil {
fmt.Fprintf(buf, "\tflex-shrink: %f;\n", *d.Shrink)
}
// TODO: Basis, Align, BreakAfter
}
fmt.Fprintf(buf, "}\n")
}
fmt.Fprintf(buf, `</style>
<div id="container">
`)
for i := range t.measured {
fmt.Fprintf(buf, "\t<div id=\"child%d\"></div>\n", i)
}
fmt.Fprintf(buf, `</div>
<pre id="out"></pre>
<script>
var out = document.getElementById("out");
var container = document.getElementById("container");
for (var i = 0; i < container.children.length; i++) {
var c = container.children[i];
var ctop = c.offsetTop - container.offsetTop;
var cleft = c.offsetLeft - container.offsetLeft;
var cbottom = ctop + c.offsetHeight;
var cright = cleft + c.offsetWidth;
out.innerHTML += "\timage.Rect(" + cleft + ", " + ctop + ", " + cright + ", " + cbottom + "),\n";
}
</script>
`)
return buf.String()
}
var colors = []color.RGBA{
{0x00, 0x7f, 0x7f, 0xff}, // Cyan
{0x7f, 0x00, 0x7f, 0xff}, // Magenta
{0x7f, 0x7f, 0x00, 0xff}, // Yellow
{0xff, 0x00, 0x00, 0xff}, // Red
{0x00, 0xff, 0x00, 0xff}, // Green
{0x00, 0x00, 0xff, 0xff}, // Blue
}
var layoutTests = []layoutTest{{
size: image.Point{100, 100},
measured: [][2]float64{{100, 100}},
want: []image.Rectangle{
image.Rect(0, 0, 100, 100),
},
}, {
size: image.Point{350, 100},
measured: [][2]float64{{100, 100}, {100, 100}, {100, 100}},
want: []image.Rectangle{
image.Rect(0, 0, 100, 100),
image.Rect(100, 0, 200, 100),
image.Rect(200, 0, 300, 100),
},
}, {
size: image.Point{300, 100},
measured: [][2]float64{{100, 100}, {100, 100}},
want: []image.Rectangle{
image.Rect(0, 0, 100, 100),
image.Rect(100, 0, 300, 100),
},
layoutData: []LayoutData{{}, {Grow: 1}},
}, {
size: image.Point{300, 100},
measured: [][2]float64{{50, 50}, {100, 100}, {100, 100}},
want: []image.Rectangle{
image.Rect(0, 0, 50, 50),
image.Rect(50, 0, 175, 100),
image.Rect(175, 0, 300, 100),
},
layoutData: []LayoutData{{}, {Grow: 1}, {Grow: 1}},
}, {
size: image.Point{300, 100},
measured: [][2]float64{{20, 100}, {20, 100}, {20, 100}},
want: []image.Rectangle{
image.Rect(0, 0, 30, 100),
image.Rect(30, 0, 130, 100),
image.Rect(130, 0, 300, 100),
},
layoutData: []LayoutData{
{MaxSize: &image.Point{30, 100}, Grow: 1},
{MinSize: image.Point{100, 0}, Grow: 1},
{Grow: 4},
},
}}
func TestLayout(t *testing.T) {
for testNum, test := range layoutTests {
w := NewFlex()
w.Direction = test.direction
w.Wrap = test.wrap
w.AlignContent = test.alignContent
var children []node.Node
for i, sz := range test.measured {
n := widget.NewUniform(colors[i], unit.Pixels(sz[0]), unit.Pixels(sz[1]))
if test.layoutData != nil {
n.LayoutData = test.layoutData[i]
}
w.AppendChild(n)
children = append(children, n)
}
w.Measure(nil)
w.Rect = image.Rectangle{Max: test.size}
w.Layout(nil)
bad := false
for i, n := range children {
if n.Wrappee().Rect != test.want[i] {
bad = true
break
}
}
if bad {
t.Logf("Bad testNum %d:\n%s", testNum, test.html())
}
for i, n := range children {
if got, want := n.Wrappee().Rect, test.want[i]; got != want {
t.Errorf("[%d].Rect=%v, want %v", i, got, want)
}
}
}
}