From b97b02942282f364c4a5f501487cd27a367c2eee Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Thu, 15 Feb 2024 17:56:53 -0800 Subject: [PATCH] Updates #22: add focus arrow key navigation to tracklist --- ui/widgets/gridviewitem.go | 2 ++ ui/widgets/listrowbase.go | 20 ++++++++++++++++++-- ui/widgets/tracklist.go | 38 ++++++++++++++++++++++++++++++++++---- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/ui/widgets/gridviewitem.go b/ui/widgets/gridviewitem.go index e4511fc..f077910 100644 --- a/ui/widgets/gridviewitem.go +++ b/ui/widgets/gridviewitem.go @@ -257,6 +257,8 @@ func (g *GridViewItem) TypedKey(e *fyne.KeyEvent) { focusArg = 3 case fyne.KeyEnter: fallthrough + case fyne.KeyReturn: + fallthrough case fyne.KeySpace: if g.OnShowItemPage != nil { g.OnShowItemPage() diff --git a/ui/widgets/listrowbase.go b/ui/widgets/listrowbase.go index afd1aca..03c5726 100644 --- a/ui/widgets/listrowbase.go +++ b/ui/widgets/listrowbase.go @@ -22,8 +22,9 @@ type ListRowBase struct { Selected bool Focused bool - OnTapped func() - OnDoubleTapped func() + OnTapped func() + OnDoubleTapped func() + OnFocusNeighbor func(up bool) //TODO: func(up, selecting bool) tappedAt int64 // unixMillis focusedRect *canvas.Rectangle @@ -57,7 +58,22 @@ func (l *ListRowBase) FocusLost() { } func (l *ListRowBase) TypedKey(e *fyne.KeyEvent) { + /** + // TODO: enable shift+arrows for selection, but it's complicated to implement in the widgets + desktop, ok := fyne.CurrentApp().Driver().(desktop.Driver) + isSelecting := func() bool { + return ok && desktop.CurrentKeyModifiers()&fyne.KeyModifierShift != 0 + } + */ switch { + case e.Name == fyne.KeyUp: + if l.OnFocusNeighbor != nil { + l.OnFocusNeighbor(true) + } + case e.Name == fyne.KeyDown: + if l.OnFocusNeighbor != nil { + l.OnFocusNeighbor(false) + } case e.Name == fyne.KeySpace: if l.OnTapped != nil { l.OnTapped() diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 47e979c..c7fb2d2 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -101,6 +101,7 @@ type Tracklist struct { tracksMutex sync.RWMutex tracks []*trackModel tracksOrigOrder []*trackModel + itemForIndex map[int]*TrackRow nowPlayingID string colLayout *layouts.ColumnsLayout @@ -117,7 +118,7 @@ type trackModel struct { } func NewTracklist(tracks []*mediaprovider.Track) *Tracklist { - t := &Tracklist{visibleColumns: make([]bool, numColumns)} + t := &Tracklist{visibleColumns: make([]bool, numColumns), itemForIndex: make(map[int]*TrackRow)} t.ExtendBaseWidget(t) if len(tracks) > 0 { @@ -143,9 +144,28 @@ func NewTracklist(tracks []*mediaprovider.Track) *Tracklist { t.lenTracks, func() fyne.CanvasObject { tr := NewTrackRow(t, playingIcon) - tr.OnTapped = func() { t.onSelectTrack(tr.trackIdx) } + tr.OnTapped = func() { + t.onSelectTrack(tr.trackIdx) + } tr.OnTappedSecondary = t.onShowContextMenu - tr.OnDoubleTapped = func() { t.onPlayTrackAt(tr.trackIdx) } + tr.OnDoubleTapped = func() { + t.onPlayTrackAt(tr.trackIdx) + } + tr.OnFocusNeighbor = func(up bool) { + focusIdx := tr.trackIdx + 1 + if up { + focusIdx = tr.trackIdx - 1 + } + if focusIdx >= 0 && focusIdx < t.lenTracks() { + t.list.ScrollTo(focusIdx) + } + t.tracksMutex.RLock() + other, ok := t.itemForIndex[focusIdx] + t.tracksMutex.RUnlock() + if ok { + fyne.CurrentApp().Driver().CanvasForObject(t).Focus(other) + } + } return tr }, func(itemID widget.ListItemID, item fyne.CanvasObject) { @@ -161,7 +181,15 @@ func NewTracklist(tracks []*mediaprovider.Track) *Tracklist { t.tracksMutex.RUnlock() tr := item.(*TrackRow) - tr.trackIdx = itemID + if tr.trackID != model.track.ID || tr.trackIdx != itemID { + t.tracksMutex.Lock() + if other, ok := t.itemForIndex[itemID]; ok && other == tr { + delete(t.itemForIndex, other.trackIdx) + } + t.itemForIndex[itemID] = tr + t.tracksMutex.Unlock() + tr.trackIdx = itemID + } i := -1 // signal that we want to display the actual track num. if t.Options.AutoNumber { i = itemID + 1 @@ -299,6 +327,7 @@ func (t *Tracklist) Clear() { defer t.tracksMutex.Unlock() t.tracks = nil t.tracksOrigOrder = nil + t.itemForIndex = make(map[int]*TrackRow) } // Sets the tracks in the tracklist. Thread-safe. @@ -310,6 +339,7 @@ func (t *Tracklist) SetTracks(trs []*mediaprovider.Track) { func (t *Tracklist) _setTracks(trs []*mediaprovider.Track) { t.tracksMutex.Lock() defer t.tracksMutex.Unlock() + t.itemForIndex = make(map[int]*TrackRow) t.tracksOrigOrder = toTrackModels(trs) t.doSortTracks() }