forked from joejulian/gio
layout: add List.Gap for spacing out items
Signed-off-by: Egon Elbre <egonelbre@gmail.com>
This commit is contained in:
+20
-7
@@ -31,6 +31,8 @@ type List struct {
|
|||||||
Alignment Alignment
|
Alignment Alignment
|
||||||
// ScrollAnyAxis allows any scroll axis to scroll the list, not just the main axis.
|
// ScrollAnyAxis allows any scroll axis to scroll the list, not just the main axis.
|
||||||
ScrollAnyAxis bool
|
ScrollAnyAxis bool
|
||||||
|
// Gap is the space in pixels between children.
|
||||||
|
Gap int
|
||||||
|
|
||||||
cs Constraints
|
cs Constraints
|
||||||
scroll gesture.Scroll
|
scroll gesture.Scroll
|
||||||
@@ -130,7 +132,7 @@ func (l *List) Layout(gtx Context, len int, w ListElement) Dimensions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if numLaidOut > 0 {
|
if numLaidOut > 0 {
|
||||||
l.Position.Length = laidOutTotalLength * len / numLaidOut
|
l.Position.Length = laidOutTotalLength*len/numLaidOut + l.Gap*(len-1)
|
||||||
} else {
|
} else {
|
||||||
l.Position.Length = 0
|
l.Position.Length = 0
|
||||||
}
|
}
|
||||||
@@ -223,11 +225,11 @@ func (l *List) nextDir() iterationDir {
|
|||||||
if len(l.children) > 0 {
|
if len(l.children) > 0 {
|
||||||
if l.Position.First > 0 {
|
if l.Position.First > 0 {
|
||||||
firstChild := l.children[0]
|
firstChild := l.children[0]
|
||||||
firstSize = l.Axis.Convert(firstChild.size).X
|
firstSize = l.Axis.Convert(firstChild.size).X + l.Gap
|
||||||
}
|
}
|
||||||
if last < l.len {
|
if last < l.len {
|
||||||
lastChild := l.children[len(l.children)-1]
|
lastChild := l.children[len(l.children)-1]
|
||||||
lastSize = l.Axis.Convert(lastChild.size).X
|
lastSize = l.Axis.Convert(lastChild.size).X + l.Gap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
@@ -245,6 +247,9 @@ func (l *List) nextDir() iterationDir {
|
|||||||
func (l *List) end(dims Dimensions, call op.CallOp) {
|
func (l *List) end(dims Dimensions, call op.CallOp) {
|
||||||
child := scrollChild{dims.Size, call}
|
child := scrollChild{dims.Size, call}
|
||||||
mainSize := l.Axis.Convert(child.size).X
|
mainSize := l.Axis.Convert(child.size).X
|
||||||
|
if len(l.children) > 0 {
|
||||||
|
l.maxSize += l.Gap
|
||||||
|
}
|
||||||
l.maxSize += mainSize
|
l.maxSize += mainSize
|
||||||
switch l.dir {
|
switch l.dir {
|
||||||
case iterateForward:
|
case iterateForward:
|
||||||
@@ -254,7 +259,7 @@ func (l *List) end(dims Dimensions, call op.CallOp) {
|
|||||||
copy(l.children[1:], l.children)
|
copy(l.children[1:], l.children)
|
||||||
l.children[0] = child
|
l.children[0] = child
|
||||||
l.Position.First--
|
l.Position.First--
|
||||||
l.Position.Offset += mainSize
|
l.Position.Offset += mainSize + l.Gap
|
||||||
default:
|
default:
|
||||||
panic("call Next before End")
|
panic("call Next before End")
|
||||||
}
|
}
|
||||||
@@ -279,7 +284,7 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
l.Position.First++
|
l.Position.First++
|
||||||
l.Position.Offset -= mainSize
|
l.Position.Offset -= mainSize + l.Gap
|
||||||
first = child
|
first = child
|
||||||
children = children[1:]
|
children = children[1:]
|
||||||
}
|
}
|
||||||
@@ -291,6 +296,9 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
|||||||
if c := sz.Y; c > maxCross {
|
if c := sz.Y; c > maxCross {
|
||||||
maxCross = c
|
maxCross = c
|
||||||
}
|
}
|
||||||
|
if i > 0 {
|
||||||
|
size += l.Gap
|
||||||
|
}
|
||||||
size += sz.X
|
size += sz.X
|
||||||
if size >= mainMax {
|
if size >= mainMax {
|
||||||
if i < len(children)-1 {
|
if i < len(children)-1 {
|
||||||
@@ -326,14 +334,19 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
|||||||
// Lay out leading invisible child.
|
// Lay out leading invisible child.
|
||||||
if first != (scrollChild{}) {
|
if first != (scrollChild{}) {
|
||||||
sz := l.Axis.Convert(first.size)
|
sz := l.Axis.Convert(first.size)
|
||||||
pos -= sz.X
|
pos -= sz.X + l.Gap
|
||||||
layout(first)
|
layout(first)
|
||||||
|
pos += l.Gap
|
||||||
|
}
|
||||||
|
for i, child := range children {
|
||||||
|
if i > 0 {
|
||||||
|
pos += l.Gap
|
||||||
}
|
}
|
||||||
for _, child := range children {
|
|
||||||
layout(child)
|
layout(child)
|
||||||
}
|
}
|
||||||
// Lay out trailing invisible child.
|
// Lay out trailing invisible child.
|
||||||
if last != (scrollChild{}) {
|
if last != (scrollChild{}) {
|
||||||
|
pos += l.Gap
|
||||||
layout(last)
|
layout(last)
|
||||||
}
|
}
|
||||||
atStart := l.Position.First == 0 && l.Position.Offset <= 0
|
atStart := l.Position.First == 0 && l.Position.Offset <= 0
|
||||||
|
|||||||
@@ -184,6 +184,93 @@ func TestListPosition(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListGap(t *testing.T) {
|
||||||
|
gtx := Context{
|
||||||
|
Ops: new(op.Ops),
|
||||||
|
Constraints: Constraints{
|
||||||
|
Max: image.Pt(100, 20),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two 10px children with 5px gap: total 25px.
|
||||||
|
l := List{Gap: 5}
|
||||||
|
dims := l.Layout(gtx, 2, func(gtx Context, idx int) Dimensions {
|
||||||
|
return Dimensions{Size: image.Pt(10, 10)}
|
||||||
|
})
|
||||||
|
if got, exp := dims.Size.X, 25; got != exp {
|
||||||
|
t.Errorf("two children with gap: got width %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Three 10px children with 5px gap: total 40px.
|
||||||
|
l = List{Gap: 5}
|
||||||
|
dims = l.Layout(gtx, 3, func(gtx Context, idx int) Dimensions {
|
||||||
|
return Dimensions{Size: image.Pt(10, 10)}
|
||||||
|
})
|
||||||
|
if got, exp := dims.Size.X, 40; got != exp {
|
||||||
|
t.Errorf("three children with gap: got width %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single child: no gap.
|
||||||
|
l = List{Gap: 5}
|
||||||
|
dims = l.Layout(gtx, 1, func(gtx Context, idx int) Dimensions {
|
||||||
|
return Dimensions{Size: image.Pt(10, 10)}
|
||||||
|
})
|
||||||
|
if got, exp := dims.Size.X, 10; got != exp {
|
||||||
|
t.Errorf("single child with gap: got width %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero children: no gap.
|
||||||
|
l = List{Gap: 5}
|
||||||
|
dims = l.Layout(gtx, 0, nil)
|
||||||
|
if got, exp := dims.Size.X, 0; got != exp {
|
||||||
|
t.Errorf("no children with gap: got width %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListGapVertical(t *testing.T) {
|
||||||
|
gtx := Context{
|
||||||
|
Ops: new(op.Ops),
|
||||||
|
Constraints: Constraints{
|
||||||
|
Max: image.Pt(20, 100),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
l := List{Axis: Vertical, Gap: 10}
|
||||||
|
dims := l.Layout(gtx, 3, func(gtx Context, idx int) Dimensions {
|
||||||
|
return Dimensions{Size: image.Pt(10, 15)}
|
||||||
|
})
|
||||||
|
// 3*15 + 2*10 = 65.
|
||||||
|
if got, exp := dims.Size.Y, 65; got != exp {
|
||||||
|
t.Errorf("vertical list with gap: got height %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListGapPosition(t *testing.T) {
|
||||||
|
gtx := Context{
|
||||||
|
Ops: new(op.Ops),
|
||||||
|
Constraints: Constraints{
|
||||||
|
Max: image.Pt(30, 20),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Viewport 30px, 5 children of 10px with 5px gap.
|
||||||
|
// Children fill: 10, 10+5+10=25, 25+5+10=40 >= 30, so 3 visible (last partially).
|
||||||
|
l := List{Gap: 5}
|
||||||
|
l.Layout(gtx, 5, func(gtx Context, idx int) Dimensions {
|
||||||
|
return Dimensions{Size: image.Pt(10, 10)}
|
||||||
|
})
|
||||||
|
if got, exp := l.Position.Count, 3; got != exp {
|
||||||
|
t.Errorf("visible count with gap: got %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
if got, exp := l.Position.First, 0; got != exp {
|
||||||
|
t.Errorf("first with gap: got %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
// OffsetLast = mainMax - size = 30 - 40 = -10.
|
||||||
|
if got, exp := l.Position.OffsetLast, -10; got != exp {
|
||||||
|
t.Errorf("offset last with gap: got %d, expected %d", got, exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExtraChildren(t *testing.T) {
|
func TestExtraChildren(t *testing.T) {
|
||||||
var l List
|
var l List
|
||||||
l.Position.First = 1
|
l.Position.First = 1
|
||||||
|
|||||||
Reference in New Issue
Block a user