mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
layout: layout one invisible child at each end of a List
A recent change added automatic scrolling to move focused widgets into view. This change modifies List to layout an extra child at each of its ends, to enable focus to move to them and trigger automatic scrolling of the list. For https://github.com/tailscale/tailscale/issues/4278. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+38
-5
@@ -181,12 +181,25 @@ func (l *List) nextDir() iterationDir {
|
||||
if l.Position.Offset < 0 && l.Position.First == 0 {
|
||||
l.Position.Offset = 0
|
||||
}
|
||||
// Lay out an extra (invisible) child at each end to enable focus to
|
||||
// move to them, triggering automatic scroll.
|
||||
firstSize, lastSize := 0, 0
|
||||
if len(l.children) > 0 {
|
||||
if l.Position.First > 0 {
|
||||
firstChild := l.children[0]
|
||||
firstSize = l.Axis.Convert(firstChild.size).X
|
||||
}
|
||||
if last < l.len {
|
||||
lastChild := l.children[len(l.children)-1]
|
||||
lastSize = l.Axis.Convert(lastChild.size).X
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case len(l.children) == l.len:
|
||||
return iterateNone
|
||||
case l.maxSize-l.Position.Offset < vsize:
|
||||
case l.maxSize-l.Position.Offset-lastSize < vsize:
|
||||
return iterateForward
|
||||
case l.Position.Offset < 0:
|
||||
case l.Position.Offset-firstSize < 0:
|
||||
return iterateBackward
|
||||
}
|
||||
return iterateNone
|
||||
@@ -219,9 +232,11 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
||||
}
|
||||
mainMin, mainMax := l.Axis.mainConstraint(l.cs)
|
||||
children := l.children
|
||||
// Skip invisible children
|
||||
var first scrollChild
|
||||
// Skip invisible children.
|
||||
for len(children) > 0 {
|
||||
sz := children[0].size
|
||||
child := children[0]
|
||||
sz := child.size
|
||||
mainSize := l.Axis.Convert(sz).X
|
||||
if l.Position.Offset < mainSize {
|
||||
// First child is partially visible.
|
||||
@@ -229,10 +244,12 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
||||
}
|
||||
l.Position.First++
|
||||
l.Position.Offset -= mainSize
|
||||
first = child
|
||||
children = children[1:]
|
||||
}
|
||||
size := -l.Position.Offset
|
||||
var maxCross int
|
||||
var last scrollChild
|
||||
for i, child := range children {
|
||||
sz := l.Axis.Convert(child.size)
|
||||
if c := sz.Y; c > maxCross {
|
||||
@@ -240,6 +257,9 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
||||
}
|
||||
size += sz.X
|
||||
if size >= mainMax {
|
||||
if i < len(children)-1 {
|
||||
last = children[i+1]
|
||||
}
|
||||
children = children[:i+1]
|
||||
break
|
||||
}
|
||||
@@ -251,7 +271,7 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
||||
if space := l.Position.OffsetLast; l.ScrollToEnd && space > 0 {
|
||||
pos += space
|
||||
}
|
||||
for _, child := range children {
|
||||
layout := func(child scrollChild) {
|
||||
sz := l.Axis.Convert(child.size)
|
||||
var cross int
|
||||
switch l.Alignment {
|
||||
@@ -281,6 +301,19 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
|
||||
cl.Pop()
|
||||
pos += childSize
|
||||
}
|
||||
// Lay out leading invisible child.
|
||||
if first != (scrollChild{}) {
|
||||
sz := l.Axis.Convert(first.size)
|
||||
pos -= sz.X
|
||||
layout(first)
|
||||
}
|
||||
for _, child := range children {
|
||||
layout(child)
|
||||
}
|
||||
// Lay out trailing invisible child.
|
||||
if last != (scrollChild{}) {
|
||||
layout(last)
|
||||
}
|
||||
atStart := l.Position.First == 0 && l.Position.Offset <= 0
|
||||
atEnd := l.Position.First+len(children) == l.len && mainMax >= pos
|
||||
if atStart && l.scrollDelta < 0 || atEnd && l.scrollDelta > 0 {
|
||||
|
||||
@@ -140,3 +140,21 @@ func TestListPosition(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtraChildren(t *testing.T) {
|
||||
var l List
|
||||
l.Position.First = 1
|
||||
gtx := Context{
|
||||
Ops: new(op.Ops),
|
||||
Constraints: Exact(image.Pt(10, 10)),
|
||||
}
|
||||
count := 0
|
||||
const all = 3
|
||||
l.Layout(gtx, all, func(gtx Context, idx int) Dimensions {
|
||||
count++
|
||||
return Dimensions{Size: image.Pt(10, 10)}
|
||||
})
|
||||
if count != all {
|
||||
t.Errorf("laid out %d of %d children", count, all)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user