diff --git a/widget/material/list.go b/widget/material/list.go index b28d6e35..6974b422 100644 --- a/widget/material/list.go +++ b/widget/material/list.go @@ -23,18 +23,39 @@ import ( // start will be less than or equal to end. func fromListPosition(lp layout.Position, elements int, majorAxisSize int) (start, end float32) { // Approximate the size of the scrollable content. - lengthPx := float32(lp.Length) - meanElementHeight := lengthPx / float32(elements) + lengthEstPx := float32(lp.Length) + elementLenEstPx := lengthEstPx / float32(elements) // Determine how much of the content is visible. listOffsetF := float32(lp.Offset) + listOffsetL := float32(lp.OffsetLast) + + // Compute the location of the beginning of the viewport using estimated element size and known + // pixel offsets. + viewportStart := clamp1((float32(lp.First)*elementLenEstPx + listOffsetF) / lengthEstPx) + viewportEnd := clamp1((float32(lp.First+lp.Count)*elementLenEstPx + listOffsetL) / lengthEstPx) + viewportFraction := viewportEnd - viewportStart + + // Compute the expected visible proportion of the list content based solely on the ratio + // of the visible size and the estimated total size. visiblePx := float32(majorAxisSize) - visibleFraction := visiblePx / lengthPx + visibleFraction := visiblePx / lengthEstPx - // Compute the location of the beginning of the viewport. - viewportStart := (float32(lp.First)*meanElementHeight + listOffsetF) / lengthPx + // Compute the error between the two methods of determining the viewport and diffuse the + // error on either end of the viewport based on how close we are to each end. + err := visibleFraction - viewportFraction + adjStart := viewportStart + adjEnd := viewportEnd + if viewportFraction < 1 { + startShare := viewportStart / (1 - viewportFraction) + endShare := (1 - viewportEnd) / (1 - viewportFraction) + startErr := startShare * err + endErr := endShare * err - return viewportStart, clamp1(viewportStart + visibleFraction) + adjStart -= startErr + adjEnd += endErr + } + return adjStart, adjEnd } // rangeIsScrollable returns whether the viewport described by start and end