Skip to content

GridWrap keyboard navigation does not change to next row at the end of previous row #5994

@coolapso

Description

@coolapso

Checklist

  • I have searched the issue tracker for open issues that relate to the same problem, before opening a new one.
  • This issue only relates to a single bug. I will open new issues for any other problems.

Describe the bug

When using keyboard to navigate on a GridWrap when the highlighted item reaches the end of a row it does not move to the row below

How to reproduce

  1. Create a gridWrap with plenty of items
  2. Navigate with the keyboard till the end of a row
  3. Observe highlight not changing

Screenshots

2025-10-26_17-10-1761496625.mp4

Example code

package main

import (
	"fmt"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/widget"
)

type FocuseableGridWrap struct {
	widget.GridWrap
	OnNavigated func(widget.GridWrapItemID)
}

func newItemData() []string {
	var listData []string
	for i := range 1000 {
		listData = append(listData, fmt.Sprintf("Item %d", i))
	}

	return listData
}

func NewFocuseableGridWrap(length func() int, createItem func() fyne.CanvasObject, updateItem func(widget.GridWrapItemID, fyne.CanvasObject)) *FocuseableGridWrap {
	grid := &FocuseableGridWrap{}
	grid.Length = length
	grid.CreateItem = createItem
	grid.UpdateItem = updateItem
	grid.ExtendBaseWidget(grid)
	return grid
}

func NewGrid() *FocuseableGridWrap {
	return NewFocuseableGridWrap(
		func() int {
			return 3
		},
		func() fyne.CanvasObject {
			return widget.NewLabel("")
		},
		func(i widget.GridWrapItemID, o fyne.CanvasObject) {
			labels := []fyne.CanvasObject{
				container.NewStack(widget.NewLabel("A")),
				container.NewStack(widget.NewLabel("B")),
				container.NewStack(widget.NewLabel("C")),
			}

			for _, l := range labels {
				l = o.(*fyne.Container)
				l.Refresh()
			}
		},
	)
}

type FocuseableList struct {
	widget.List
}

func (l *FocuseableList) TypedKey(event *fyne.KeyEvent) {
	switch event.Name {
	case fyne.KeyEnd:
		l.ScrollToBottom()
	case fyne.KeyHome:
		l.ScrollToTop()
	default:
		l.List.TypedKey(event)
	}
}

func NewFocuseableList(length func() int, createItem func() fyne.CanvasObject, updateItem func(widget.ListItemID, fyne.CanvasObject)) *FocuseableList {
	list := &FocuseableList{}
	list.Length = length
	list.CreateItem = createItem
	list.UpdateItem = updateItem
	list.ExtendBaseWidget(list)
	return list
}

func main() {
	a := app.NewWithID("foo")
	w := a.NewWindow("bar")
	w.SetContent(widget.NewCard("foo", "bar", nil))

	gridData := newItemData()
	grid := NewFocuseableGridWrap(
		func() int {
			return len(gridData)
		},
		func() fyne.CanvasObject {
			return widget.NewLabel("template")
		},
		func(i widget.GridWrapItemID, o fyne.CanvasObject) {
			o.(*widget.Label).SetText(gridData[i])
		},
	)
	grid.OnHighlighted = func(id widget.GridWrapItemID) {
		fmt.Println("grid item", id, "highlighted")
	}

	tree := widget.NewTree(
		func(id widget.TreeNodeID) []widget.TreeNodeID {
			switch id {
			case "":
				return []widget.TreeNodeID{"a", "b", "c"}
			case "a":
				return []widget.TreeNodeID{"a1", "a2"}
			}
			return []string{}
		},
		func(id widget.TreeNodeID) bool {
			return id == "" || id == "a"
		},
		func(branch bool) fyne.CanvasObject {
			if branch {
				return widget.NewLabel("Branch template")
			}
			return widget.NewLabel("Leaf template")
		},
		func(id widget.TreeNodeID, branch bool, o fyne.CanvasObject) {
			text := id
			if branch {
				text += " (branch)"
			}
			o.(*widget.Label).SetText(text)
		},
	)

	tree.OnHighlighted = func(id widget.TreeNodeID) {
		fmt.Println("Tree item", id, "highlighted")
	}

	table := widget.NewTableWithHeaders(
		func() (int, int) { return 500, 150 },
		func() fyne.CanvasObject {
			return widget.NewLabel("Cell 000, 000")
		},
		func(id widget.TableCellID, cell fyne.CanvasObject) {
			label := cell.(*widget.Label)
			switch id.Col {
			case 0:
				label.SetText("A longer cell")
			default:
				label.SetText(fmt.Sprintf("Cell %d, %d", id.Row+1, id.Col+1))
			}
		})
	table.SetColumnWidth(0, 102)
	table.SetRowHeight(2, 50)

	table.OnHighlighted = func(id widget.TableCellID) {
		fmt.Println("table cell", id, "highlighted")
	}

	listData := newItemData()
	list := NewFocuseableList(
		func() int {
			return len(listData)
		},
		func() fyne.CanvasObject {
			return widget.NewLabel("template")
		},
		func(i widget.ListItemID, o fyne.CanvasObject) {
			o.(*widget.Label).SetText(listData[i])
		},
	)

	list.OnHighlighted = func(id widget.ListItemID) {
		fmt.Println("list item", id, "highlighted")
	}

	content := container.New(layout.NewGridLayout(2), grid, tree, container.NewScroll(table), list)
	w.SetContent(content)
	w.Resize(fyne.NewSize(1280, 720))
	w.ShowAndRun()
}

Fyne version

develop also present on 1.6.3

Go compiler version

1.25.3

Operating system and version

Arch linux

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions