Skip to content

Commit

Permalink
Merge pull request #5477 from andydotxyz/fix/5456
Browse files Browse the repository at this point in the history
  • Loading branch information
andydotxyz authored Feb 1, 2025
2 parents c098d33 + bb2297d commit 6503f9e
Show file tree
Hide file tree
Showing 14 changed files with 134 additions and 73 deletions.
10 changes: 5 additions & 5 deletions container/scroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ type Scroll = widget.Scroll
// ScrollDirection represents the directions in which a Scroll container can scroll its child content.
//
// Since: 1.4
type ScrollDirection = widget.ScrollDirection
type ScrollDirection = fyne.ScrollDirection

// Constants for valid values of ScrollDirection.
const (
// ScrollBoth supports horizontal and vertical scrolling.
ScrollBoth ScrollDirection = widget.ScrollBoth
ScrollBoth ScrollDirection = fyne.ScrollBoth
// ScrollHorizontalOnly specifies the scrolling should only happen left to right.
ScrollHorizontalOnly = widget.ScrollHorizontalOnly
ScrollHorizontalOnly = fyne.ScrollHorizontalOnly
// ScrollVerticalOnly specifies the scrolling should only happen top to bottom.
ScrollVerticalOnly = widget.ScrollVerticalOnly
ScrollVerticalOnly = fyne.ScrollVerticalOnly
// ScrollNone turns off scrolling for this container.
//
// Since: 2.1
ScrollNone = widget.ScrollNone
ScrollNone = fyne.ScrollNone
)

// NewScroll creates a scrollable parent wrapping the specified content.
Expand Down
4 changes: 2 additions & 2 deletions internal/widget/scroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// ScrollDirection represents the directions in which a Scroll can scroll its child content.
type ScrollDirection int
type ScrollDirection = fyne.ScrollDirection

// Constants for valid values of ScrollDirection.
const (
Expand Down Expand Up @@ -437,7 +437,7 @@ func (s *Scroll) ScrollToBottom() {

// ScrollToTop will scroll content to container top
func (s *Scroll) ScrollToTop() {
s.scrollBy(0, -s.Offset.Y)
s.ScrollToOffset(fyne.Position{})
s.refreshBars()
}

Expand Down
8 changes: 7 additions & 1 deletion internal/widget/scroller_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,14 @@ func TestScrollContainer_MinSize(t *testing.T) {

func TestScrollContainer_ScrollToTop(t *testing.T) {
rect := canvas.NewRectangle(color.Black)
rect.SetMinSize(fyne.NewSize(500, 50))
rect.SetMinSize(fyne.NewSize(500, 150))
scroll := NewScroll(rect)
scroll.Resize(fyne.NewSize(25, 25))

tmpOffset := fyne.NewPos(25, 50)
scroll.ScrollToOffset(tmpOffset)
assert.Equal(t, tmpOffset, scroll.Offset)

scroll.ScrollToTop()
Y := scroll.Offset.Y
assert.Equal(t, float32(0), Y)
Expand Down
18 changes: 18 additions & 0 deletions scroll.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package fyne

// ScrollDirection represents the directions in which a scrollable container or widget can scroll its child content.
//
// Since: 2.6
type ScrollDirection int

// Constants for valid values of ScrollDirection used in containers and widgets.
const (
// ScrollBoth supports horizontal and vertical scrolling.
ScrollBoth ScrollDirection = iota
// ScrollHorizontalOnly specifies the scrolling should only happen left to right.
ScrollHorizontalOnly
// ScrollVerticalOnly specifies the scrolling should only happen top to bottom.
ScrollVerticalOnly
// ScrollNone turns off scrolling for this container.
ScrollNone
)
2 changes: 1 addition & 1 deletion widget/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type Entry struct {
// Scroll can be used to turn off the scrolling of our entry when Wrapping is WrapNone.
//
// Since: 2.4
Scroll widget.ScrollDirection
Scroll fyne.ScrollDirection

// Set a validator that this entry will check against
// Since: 1.4
Expand Down
18 changes: 5 additions & 13 deletions widget/gridwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,21 +214,14 @@ func (l *GridWrap) ScrollTo(id GridWrapItemID) {

// ScrollToBottom scrolls to the end of the list
func (l *GridWrap) ScrollToBottom() {
length := 0
if f := l.Length; f != nil {
length = f()
}
if length > 0 {
length--
}
l.scrollTo(GridWrapItemID(length))
l.Refresh()
l.scroller.ScrollToBottom()
l.offsetUpdated(l.scroller.Offset)
}

// ScrollToTop scrolls to the start of the list
func (l *GridWrap) ScrollToTop() {
l.scrollTo(0)
l.Refresh()
l.scroller.ScrollToTop()
l.offsetUpdated(l.scroller.Offset)
}

// ScrollToOffset scrolls the list to the given offset position
Expand All @@ -246,9 +239,8 @@ func (l *GridWrap) ScrollToOffset(offset float32) {
if offset > contentHeight {
offset = contentHeight
}
l.scroller.Offset.Y = offset
l.scroller.ScrollToOffset(fyne.NewPos(0, offset))
l.offsetUpdated(l.scroller.Offset)
l.Refresh()
}

// TypedKey is called if a key event happens while this GridWrap is focused.
Expand Down
18 changes: 5 additions & 13 deletions widget/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,23 +260,16 @@ func (l *List) ScrollTo(id ListItemID) {
//
// Since: 2.1
func (l *List) ScrollToBottom() {
length := 0
if f := l.Length; f != nil {
length = f()
}
if length > 0 {
length--
}
l.scrollTo(length)
l.Refresh()
l.scroller.ScrollToBottom()
l.offsetUpdated(l.scroller.Offset)
}

// ScrollToTop scrolls to the start of the list
//
// Since: 2.1
func (l *List) ScrollToTop() {
l.scrollTo(0)
l.Refresh()
l.scroller.ScrollToTop()
l.offsetUpdated(l.scroller.Offset)
}

// ScrollToOffset scrolls the list to the given offset position.
Expand All @@ -296,9 +289,8 @@ func (l *List) ScrollToOffset(offset float32) {
if offset > contentHeight {
offset = contentHeight
}
l.scroller.Offset.Y = offset
l.scroller.ScrollToOffset(fyne.NewPos(0, offset))
l.offsetUpdated(l.scroller.Offset)
l.Refresh()
}

// GetScrollOffset returns the current scroll offset position
Expand Down
5 changes: 5 additions & 0 deletions widget/list_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ func TestList_ScrollToBottom(t *testing.T) {
func TestList_ScrollToTop(t *testing.T) {
list := createList(1000)

tmpOffset := float32(50)
list.ScrollToOffset(tmpOffset)
assert.Equal(t, tmpOffset, list.offsetY)
assert.Equal(t, tmpOffset, list.scroller.Offset.Y)

offset := float32(0)
list.ScrollToTop()
assert.Equal(t, offset, list.offsetY)
Expand Down
2 changes: 1 addition & 1 deletion widget/richtext.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type RichText struct {
BaseWidget
Segments []RichTextSegment
Wrapping fyne.TextWrap
Scroll widget.ScrollDirection
Scroll fyne.ScrollDirection

// The truncation mode of the text
//
Expand Down
13 changes: 13 additions & 0 deletions widget/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,19 @@ func (t *Table) ScrollToLeading() {
t.finishScroll()
}

// ScrollToOffset scrolls the table to a specific position
//
// Since: 2.6
func (t *Table) ScrollToOffset(off fyne.Position) {
if t.content == nil {
return
}

t.content.ScrollToOffset(off)
t.offset = t.content.Offset
t.finishScroll()
}

// ScrollToTop scrolls to the first row in the table
//
// Since: 2.1
Expand Down
27 changes: 27 additions & 0 deletions widget/table_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,33 @@ func TestTable_ScrollToLeading(t *testing.T) {
assert.Equal(t, want, table.content.Offset)
}

func TestTable_ScrollToOffset(t *testing.T) {
test.NewTempApp(t)

const (
maxRows int = 6
maxCols int = 10
width float32 = 50
height float32 = 50
)

templ := canvas.NewRectangle(color.Gray16{})
templ.SetMinSize(fyne.Size{Width: width, Height: height})

table := NewTable(
func() (int, int) { return maxRows, maxCols },
func() fyne.CanvasObject { return templ },
func(TableCellID, fyne.CanvasObject) {})

w := test.NewWindow(table)
defer w.Close()

want := fyne.NewPos(48, 25)
table.ScrollToOffset(want)
assert.Equal(t, want, table.offset)
assert.Equal(t, want, table.content.Offset)
}

func TestTable_ScrollToTop(t *testing.T) {
test.NewTempApp(t)

Expand Down
2 changes: 1 addition & 1 deletion widget/textgrid.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type TextGrid struct {
// Scroll can be used to turn off the scrolling of our TextGrid.
//
// Since: 2.6
Scroll widget.ScrollDirection
Scroll fyne.ScrollDirection
}

// Append will add new lines to the end of this TextGrid.
Expand Down
57 changes: 21 additions & 36 deletions widget/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,8 @@ func (t *Tree) ScrollToBottom() {
return
}

y, size := t.findBottom()
t.scroller.Offset.Y = y + size.Height - t.scroller.Size().Height

t.scroller.ScrollToBottom()
t.offsetUpdated(t.scroller.Offset)
t.Refresh()
}

// ScrollTo scrolls to the node with the given id.
Expand All @@ -267,14 +264,30 @@ func (t *Tree) ScrollTo(uid TreeNodeID) {
}

// TODO scrolling to a node should open all parents if they aren't already
newY := t.scroller.Offset.Y
if y < t.scroller.Offset.Y {
t.scroller.Offset.Y = y
newY = y
} else if y+size.Height > t.scroller.Offset.Y+t.scroller.Size().Height {
t.scroller.Offset.Y = y + size.Height - t.scroller.Size().Height
newY = y + size.Height - t.scroller.Size().Height
}

t.scroller.ScrollToOffset(fyne.NewPos(0, newY))
t.offsetUpdated(t.scroller.Offset)
}

// ScrollToOffset scrolls the tree to the given offset position.
//
// Since: 2.6
func (t *Tree) ScrollToOffset(offset float32) {
if t.scroller == nil {
return
}
if offset < 0 {
offset = 0
}

t.scroller.ScrollToOffset(fyne.NewPos(0, offset))
t.offsetUpdated(t.scroller.Offset)
t.Refresh()
}

// ScrollToTop scrolls to the top of the tree.
Expand All @@ -285,9 +298,8 @@ func (t *Tree) ScrollToTop() {
return
}

t.scroller.Offset.Y = 0
t.scroller.ScrollToTop()
t.offsetUpdated(t.scroller.Offset)
t.Refresh()
}

// Select marks the specified node to be selected.
Expand Down Expand Up @@ -428,33 +440,6 @@ func (t *Tree) ensureOpenMap() {
}
}

func (t *Tree) findBottom() (y float32, size fyne.Size) {
sep := t.Theme().Size(theme.SizeNamePadding)
t.walkAll(func(id, _ TreeNodeID, branch bool, _ int) {
size = t.leafMinSize
if branch {
size = t.branchMinSize
}

// Root node is not rendered unless it has been customized
if t.Root == "" && id == "" {
// This is root node, skip
return
}

// If this is not the first item, add a separator
if y > 0 {
y += sep
}

y += size.Height
})
if y > 0 {
y -= sep
}
return
}

func (t *Tree) offsetAndSize(uid TreeNodeID) (y float32, size fyne.Size, found bool) {
pad := t.Theme().Size(theme.SizeNamePadding)

Expand Down
23 changes: 23 additions & 0 deletions widget/tree_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,29 @@ func TestTree_ScrollToBottom(t *testing.T) {
assert.Equal(t, want, tree.scroller.Offset.Y)
}

func TestTree_ScrollOffset(t *testing.T) {
test.NewTempApp(t)
test.ApplyTheme(t, test.NewTheme())

data := make(map[string][]string)
addTreePath(data, "A")
addTreePath(data, "B", "C")
addTreePath(data, "D", "E", "F")
tree := NewTreeWithStrings(data)
tree.OpenBranch("B")
tree.OpenBranch("D")
tree.OpenBranch("E")

w := test.NewWindow(tree)
defer w.Close()

w.Resize(fyne.NewSize(100, 100))
tree.ScrollToOffset(20)

assert.Equal(t, float32(20), tree.offset.Y)
assert.Equal(t, float32(20), tree.scroller.Offset.Y)
}

func TestTree_ScrollToSelection(t *testing.T) {
data := make(map[string][]string)
addTreePath(data, "A")
Expand Down

0 comments on commit 6503f9e

Please sign in to comment.