diff --git a/go.mod b/go.mod index a612a5f..b79831f 100644 --- a/go.mod +++ b/go.mod @@ -44,4 +44,4 @@ require ( honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect ) -replace fyne.io/fyne/v2 v2.2.4 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230217050145-ca8ffa6896d6 +replace fyne.io/fyne/v2 v2.2.4 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230304215136-47240a9a5b0a diff --git a/go.sum b/go.sum index df49dbe..d4dfde2 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,8 @@ github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7h github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230217050145-ca8ffa6896d6 h1:iqaXkEOMiYtKXYuMUd1JTXoNTIIGfv+Bc1bAzNmsN/c= -github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230217050145-ca8ffa6896d6/go.mod h1:dl/M+f0r5lJRhy+gdPbmYy97tbu3SWF/RF6/9KXMs+s= +github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230304215136-47240a9a5b0a h1:DzABPDAu62cN2rn5JJ1mfqrXACzVHxVx2d1z2C+0020= +github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20230304215136-47240a9a5b0a/go.mod h1:dl/M+f0r5lJRhy+gdPbmYy97tbu3SWF/RF6/9KXMs+s= github.com/dweymouth/go-subsonic v0.0.0-20230210044542-537b9238299b h1:8JbTKYDdQg6JKu7ZDbEaVbXxutc3pRF58yNvTVMBHec= github.com/dweymouth/go-subsonic v0.0.0-20230210044542-537b9238299b/go.mod h1:fUez6NFiEJiQTZizZ1BThZr5GJXAbigzGYjEPNm4tdI= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/ui/widgets/listrowbase.go b/ui/widgets/listrowbase.go new file mode 100644 index 0000000..3c09f98 --- /dev/null +++ b/ui/widgets/listrowbase.go @@ -0,0 +1,94 @@ +package widgets + +import ( + "time" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +var _ fyne.Tappable = (*ListRowBase)(nil) +var _ fyne.Widget = (*ListRowBase)(nil) +var _ fyne.Focusable = (*ListRowBase)(nil) + +type ListRowBase struct { + widget.BaseWidget + + Content fyne.CanvasObject + Selected bool + Focused bool + + OnTapped func() + OnDoubleTapped func() + + tappedAt int64 // unixMillis + focusedRect *canvas.Rectangle + selectionRect *canvas.Rectangle +} + +// We implement our own double tapping so that the Tapped behavior +// can be triggered instantly. +func (l *ListRowBase) Tapped(*fyne.PointEvent) { + prevTap := l.tappedAt + l.tappedAt = time.Now().UnixMilli() + if l.tappedAt-prevTap < 300 { + if l.OnDoubleTapped != nil { + l.OnDoubleTapped() + } + } else { + if l.OnTapped != nil { + l.OnTapped() + } + } +} + +func (l *ListRowBase) FocusGained() { + l.Focused = true + l.Refresh() +} + +func (l *ListRowBase) FocusLost() { + l.Focused = false + l.Refresh() +} + +func (l *ListRowBase) TypedKey(e *fyne.KeyEvent) { + switch { + case e.Name == fyne.KeySpace: + if l.OnTapped != nil { + l.OnTapped() + } + case e.Name == fyne.KeyReturn || e.Name == fyne.KeyEnter: + if l.OnDoubleTapped != nil { + l.OnDoubleTapped() + } else if l.OnTapped != nil { + l.OnTapped() + } + } +} + +func (l *ListRowBase) TypedRune(r rune) { +} + +func (l *ListRowBase) Refresh() { + l.focusedRect.Hidden = !l.Focused + l.selectionRect.Hidden = !l.Selected + l.BaseWidget.Refresh() +} + +func (l *ListRowBase) CreateRenderer() fyne.WidgetRenderer { + if l.selectionRect == nil { + l.selectionRect = canvas.NewRectangle(theme.SelectionColor()) + l.selectionRect.Hidden = !l.Selected + } + if l.focusedRect == nil { + l.focusedRect = canvas.NewRectangle(theme.HoverColor()) + l.focusedRect.Hidden = !l.Focused + } + return widget.NewSimpleRenderer( + container.NewMax(l.selectionRect, l.focusedRect, l.Content), + ) +} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 870f455..23fd1ed 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -12,7 +12,6 @@ import ( "time" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/driver/desktop" "fyne.io/fyne/v2/theme" @@ -89,7 +88,7 @@ func NewTracklist(tracks []*subsonic.Child) *Tracklist { func(itemID widget.ListItemID, item fyne.CanvasObject) { tr := item.(*TrackRow) tr.trackIdx = itemID - tr.selectionRect.Hidden = !t.selectionMgr.IsSelected(itemID) + tr.Selected = t.selectionMgr.IsSelected(itemID) i := -1 // signal that we want to display the actual track num. if t.AutoNumber { i = itemID + 1 @@ -343,7 +342,7 @@ func colName(i int) string { } type TrackRow struct { - widget.BaseWidget + ListRowBase // internal state tracklist *Tracklist @@ -355,7 +354,6 @@ type TrackRow struct { isPlaying bool isFavorite bool playCount int64 - tappedAt int64 // unixMillis num *widget.RichText name *widget.RichText @@ -367,13 +365,9 @@ type TrackRow struct { bitrate *widget.RichText plays *widget.RichText - OnTapped func() - OnDoubleTapped func() OnTappedSecondary func(e *fyne.PointEvent, trackIdx int) - playingIcon fyne.CanvasObject - selectionRect *canvas.Rectangle - container *fyne.Container + playingIcon fyne.CanvasObject } func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow { @@ -399,11 +393,8 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow t.bitrate = widget.NewRichTextWithText("") t.bitrate.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing - t.selectionRect = canvas.NewRectangle(theme.SelectionColor()) - t.selectionRect.Hidden = true - t.container = container.NewMax(t.selectionRect, - container.New(tracklist.colLayout, - t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.plays, t.bitrate)) + t.Content = container.New(tracklist.colLayout, + t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.plays, t.bitrate) return t } @@ -411,6 +402,10 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { // Update info that can change if this row is bound to // a new track (*subsonic.Child) if tr.ID != t.trackID { + if t.Focused { + fyne.CurrentApp().Driver().CanvasForObject(t).Focus(nil) + t.Focused = false + } t.trackID = tr.ID t.artistID = tr.ArtistID t.albumID = tr.AlbumID @@ -450,9 +445,9 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { t.bitrate.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying if isPlaying { - t.container.Objects[1].(*fyne.Container).Objects[0] = container.NewCenter(t.playingIcon) + t.Content.(*fyne.Container).Objects[0] = container.NewCenter(t.playingIcon) } else { - t.container.Objects[1].(*fyne.Container).Objects[0] = t.num + t.Content.(*fyne.Container).Objects[0] = t.num } } @@ -497,26 +492,6 @@ func (t *TrackRow) toggleFavorited() { } } -func (t *TrackRow) CreateRenderer() fyne.WidgetRenderer { - return widget.NewSimpleRenderer(t.container) -} - -// We implement our own double tapping so that the Tapped behavior -// can be triggered instantly. -func (t *TrackRow) Tapped(*fyne.PointEvent) { - prevTap := t.tappedAt - t.tappedAt = time.Now().UnixMilli() - if t.tappedAt-prevTap < 300 { - if t.OnDoubleTapped != nil { - t.OnDoubleTapped() - } - } else { - if t.OnTapped != nil { - t.OnTapped() - } - } -} - func (t *TrackRow) TappedSecondary(e *fyne.PointEvent) { if t.OnTappedSecondary != nil { t.OnTappedSecondary(e, t.trackIdx)