Add tool tips to tracklist names, etc + refactoring

This commit is contained in:
Drew Weymouth
2024-08-05 17:53:01 -07:00
parent 7c94a4c694
commit 66b8d4bc89
11 changed files with 200 additions and 84 deletions
+2 -2
View File
@@ -10,7 +10,7 @@ require (
github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1
github.com/dweymouth/fyne-advanced-list v0.0.0-20240623145729-9c6b8f99bcfe github.com/dweymouth/fyne-advanced-list v0.0.0-20240623145729-9c6b8f99bcfe
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64 github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64
github.com/dweymouth/fyne-tooltip v0.1.1 github.com/dweymouth/fyne-tooltip v0.2.0
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645 github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645
github.com/dweymouth/go-mpv v0.0.0-20240724002347-c5e5b36f1bbf github.com/dweymouth/go-mpv v0.0.0-20240724002347-c5e5b36f1bbf
github.com/dweymouth/go-subsonic v0.0.0-20240726004217-2e8e348ad417 github.com/dweymouth/go-subsonic v0.0.0-20240726004217-2e8e348ad417
@@ -54,4 +54,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace fyne.io/fyne/v2 v2.5.0 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240728183020-7abcad1f4139 replace fyne.io/fyne/v2 v2.5.0 => github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240805144743-24df77c3cc7e
+4 -4
View File
@@ -79,10 +79,10 @@ github.com/dweymouth/fyne-advanced-list v0.0.0-20240623145729-9c6b8f99bcfe h1:ow
github.com/dweymouth/fyne-advanced-list v0.0.0-20240623145729-9c6b8f99bcfe/go.mod h1:sbOhla4VcfFb4OjXiUFTLXMPTnhRUlVrDMhB8HtWR4o= github.com/dweymouth/fyne-advanced-list v0.0.0-20240623145729-9c6b8f99bcfe/go.mod h1:sbOhla4VcfFb4OjXiUFTLXMPTnhRUlVrDMhB8HtWR4o=
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64 h1:RUIrnGY034rDMlcOui/daurwX5b+52KdUKhH9aXaDSg= github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64 h1:RUIrnGY034rDMlcOui/daurwX5b+52KdUKhH9aXaDSg=
github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64/go.mod h1:3YrjFDHMlhCsSZ/OvmJCxWm9QHSgOVWZBxnraZz9Z7c= github.com/dweymouth/fyne-lyrics v0.0.0-20240528234907-15eee7ce5e64/go.mod h1:3YrjFDHMlhCsSZ/OvmJCxWm9QHSgOVWZBxnraZz9Z7c=
github.com/dweymouth/fyne-tooltip v0.1.1 h1:NmRZLlxftWHUStx3lvx/oHOc2Yg0nNdNsYHdImAq33Y= github.com/dweymouth/fyne-tooltip v0.2.0 h1:6Zy3gryctuPoQfYf8Xp3tjenioebMt11NBGW/QXIvxE=
github.com/dweymouth/fyne-tooltip v0.1.1/go.mod h1:zEgy7p9tSVIuy2GufFbOCoK3Q04zhyDPOotlU4G3Ma4= github.com/dweymouth/fyne-tooltip v0.2.0/go.mod h1:zEgy7p9tSVIuy2GufFbOCoK3Q04zhyDPOotlU4G3Ma4=
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240728183020-7abcad1f4139 h1:JNOd+yJlurYrdAuOiibR6/EYl+/+idFkk+wwyXiNwIQ= github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240805144743-24df77c3cc7e h1:FSTLNY9xV0+4/x9jKPXqUpwPZfhkAMz5ZnzLBkRirMw=
github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240728183020-7abcad1f4139/go.mod h1:9D4oT3NWeG+MLi/lP7ItZZyujHC/qqMJpoGTAYX5Uqc= github.com/dweymouth/fyne/v2 v2.3.0-rc1.0.20240805144743-24df77c3cc7e/go.mod h1:9D4oT3NWeG+MLi/lP7ItZZyujHC/qqMJpoGTAYX5Uqc=
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645 h1:KzqSaQwG3HsTZQlEtkp0BeUy9vmYZ0rq0B15qIPSiBs= github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645 h1:KzqSaQwG3HsTZQlEtkp0BeUy9vmYZ0rq0B15qIPSiBs=
github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645/go.mod h1:fcUagHBaQnt06GmBAllNE0J4O/7064zXRWdqnTTtVjI= github.com/dweymouth/go-jellyfin v0.0.0-20240517151952-5ceca61cb645/go.mod h1:fcUagHBaQnt06GmBAllNE0J4O/7064zXRWdqnTTtVjI=
github.com/dweymouth/go-mpv v0.0.0-20240724002347-c5e5b36f1bbf h1:QVjXWx7XGkSPOCEu5EL9QVY3fkdDSnG5KEkok5o1svM= github.com/dweymouth/go-mpv v0.0.0-20240724002347-c5e5b36f1bbf h1:QVjXWx7XGkSPOCEu5EL9QVY3fkdDSnG5KEkok5o1svM=
+38 -27
View File
@@ -50,7 +50,8 @@ type Controller struct {
RefreshPageFunc func() RefreshPageFunc func()
popUpQueueMutex sync.Mutex popUpQueueMutex sync.Mutex
popUpQueue *widgets.PlayQueueList popUpQueue *widget.PopUp
popUpQueueList *widgets.PlayQueueList
popUpQueueLastUsed int64 popUpQueueLastUsed int64
escapablePopUp *widget.PopUp escapablePopUp *widget.PopUp
haveModal bool haveModal bool
@@ -65,18 +66,22 @@ func New(app *backend.App, appVersion string, mainWindow fyne.Window) *Controlle
} }
c.initVisualizations() c.initVisualizations()
c.App.PlaybackManager.OnQueueChange(func() { c.App.PlaybackManager.OnQueueChange(func() {
c.popUpQueueMutex.Lock()
defer c.popUpQueueMutex.Unlock()
if c.popUpQueue != nil { if c.popUpQueue != nil {
c.popUpQueue.SetItems(c.App.PlaybackManager.GetPlayQueue()) c.popUpQueueList.SetItems(c.App.PlaybackManager.GetPlayQueue())
} }
}) })
c.App.PlaybackManager.OnSongChange(func(track mediaprovider.MediaItem, _ *mediaprovider.Track) { c.App.PlaybackManager.OnSongChange(func(track mediaprovider.MediaItem, _ *mediaprovider.Track) {
c.popUpQueueMutex.Lock()
defer c.popUpQueueMutex.Unlock()
if c.popUpQueue == nil { if c.popUpQueue == nil {
return return
} }
if track == nil { if track == nil {
c.popUpQueue.SetNowPlaying("") c.popUpQueueList.SetNowPlaying("")
} else { } else {
c.popUpQueue.SetNowPlaying(track.Metadata().ID) c.popUpQueueList.SetNowPlaying(track.Metadata().ID)
} }
}) })
return c return c
@@ -116,10 +121,28 @@ func (m *Controller) HaveModal() bool {
func (m *Controller) ShowPopUpPlayQueue() { func (m *Controller) ShowPopUpPlayQueue() {
m.popUpQueueMutex.Lock() m.popUpQueueMutex.Lock()
if m.popUpQueue == nil { if m.popUpQueue == nil {
m.popUpQueue = widgets.NewPlayQueueList(m.App.ImageManager, false) m.popUpQueueList = widgets.NewPlayQueueList(m.App.ImageManager, false)
m.popUpQueue.Reorderable = true m.popUpQueueList.Reorderable = true
m.popUpQueue.SetItems(m.App.PlaybackManager.GetPlayQueue()) m.popUpQueueList.SetItems(m.App.PlaybackManager.GetPlayQueue())
m.ConnectPlayQueuelistActions(m.popUpQueue) m.ConnectPlayQueuelistActions(m.popUpQueueList)
title := widget.NewRichTextWithText(lang.L("Play Queue"))
title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter
title.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true
ctr := container.NewBorder(title, nil, nil, nil,
container.NewPadded(m.popUpQueueList),
)
m.popUpQueue = widget.NewPopUp(ctr, m.MainWindow.Canvas())
fynetooltip.AddPopUpToolTipLayer(m.popUpQueue)
container.NewThemeOverride(m.popUpQueue, myTheme.WithColorTransformOverride(
theme.ColorNameOverlayBackground,
func(c color.Color) color.Color {
c_ := c.(color.NRGBA)
c_.A = 245
return c_
},
))
// free popUpQueue if it hasn't been used in awhile // free popUpQueue if it hasn't been used in awhile
go func() { go func() {
@@ -128,7 +151,9 @@ func (m *Controller) ShowPopUpPlayQueue() {
m.popUpQueueMutex.Lock() m.popUpQueueMutex.Lock()
now := time.Now().UnixMilli() now := time.Now().UnixMilli()
if m.popUpQueueLastUsed < now-120_000 /*2 min*/ { if m.popUpQueueLastUsed < now-120_000 /*2 min*/ {
fynetooltip.DestroyPopUpToolTipLayer(m.popUpQueue)
m.popUpQueue = nil m.popUpQueue = nil
m.popUpQueueList = nil
m.popUpQueueLastUsed = 0 m.popUpQueueLastUsed = 0
m.popUpQueueMutex.Unlock() m.popUpQueueMutex.Unlock()
t.Stop() t.Stop()
@@ -139,31 +164,17 @@ func (m *Controller) ShowPopUpPlayQueue() {
}() }()
} }
m.popUpQueueLastUsed = time.Now().UnixMilli() m.popUpQueueLastUsed = time.Now().UnixMilli()
popUpQueue := m.popUpQueue popUpQueueList := m.popUpQueueList
pop := m.popUpQueue
m.popUpQueueMutex.Unlock() m.popUpQueueMutex.Unlock()
npID := "" npID := ""
if np := m.App.PlaybackManager.NowPlaying(); np != nil { if np := m.App.PlaybackManager.NowPlaying(); np != nil {
npID = np.Metadata().ID npID = np.Metadata().ID
} }
popUpQueue.SetNowPlaying(npID) popUpQueueList.SetNowPlaying(npID)
popUpQueue.UnselectAll() popUpQueueList.UnselectAll()
title := widget.NewRichTextWithText(lang.L("Play Queue"))
title.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignCenter
title.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = true
ctr := container.NewBorder(title, nil, nil, nil,
container.NewPadded(m.popUpQueue),
)
pop := widget.NewPopUp(ctr, m.MainWindow.Canvas())
container.NewThemeOverride(pop, myTheme.WithColorTransformOverride(
theme.ColorNameOverlayBackground,
func(c color.Color) color.Color {
c_ := c.(color.NRGBA)
c_.A = 245
return c_
},
))
m.ClosePopUpOnEscape(pop) m.ClosePopUpOnEscape(pop)
minSize := fyne.NewSize(300, 400) minSize := fyne.NewSize(300, 400)
maxSize := fyne.NewSize(800, 1000) maxSize := fyne.NewSize(800, 1000)
@@ -172,7 +183,7 @@ func (m *Controller) ShowPopUpPlayQueue() {
fyne.NewSize(canvasSize.Width*0.4, canvasSize.Height*0.5), fyne.NewSize(canvasSize.Width*0.4, canvasSize.Height*0.5),
)) ))
pop.Resize(size) pop.Resize(size)
popUpQueue.ScrollToNowPlaying() // must come after resize popUpQueueList.ScrollToNowPlaying() // must come after resize
pop.ShowAtPosition(fyne.NewPos( pop.ShowAtPosition(fyne.NewPos(
canvasSize.Width-size.Width-10, canvasSize.Width-size.Width-10,
canvasSize.Height-size.Height-100, canvasSize.Height-size.Height-100,
+22
View File
@@ -12,10 +12,12 @@ import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/lang" "fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/res" "github.com/dweymouth/supersonic/res"
"github.com/dweymouth/supersonic/sharedutil" "github.com/dweymouth/supersonic/sharedutil"
@@ -252,6 +254,18 @@ func NewTruncatingLabel() *widget.Label {
return rt return rt
} }
func NewTruncatingTooltipRichText() *ttwidget.RichText {
rt := ttwidget.NewRichTextWithText("")
rt.Truncation = fyne.TextTruncateEllipsis
return rt
}
func NewTruncatingTooltipLabel() *ttwidget.Label {
rt := ttwidget.NewLabel("")
rt.Truncation = fyne.TextTruncateEllipsis
return rt
}
func NewTrailingAlignLabel() *widget.Label { func NewTrailingAlignLabel() *widget.Label {
rt := widget.NewLabel("") rt := widget.NewLabel("")
rt.Alignment = fyne.TextAlignTrailing rt.Alignment = fyne.TextAlignTrailing
@@ -269,6 +283,14 @@ func LocalizeSlice(s []string) []string {
return sharedutil.MapSlice(s, func(s string) string { return lang.L(s) }) return sharedutil.MapSlice(s, func(s string) string { return lang.L(s) })
} }
type ToolTipRichText struct {
ttwidget.RichText
OnMouseIn func(e *desktop.MouseEvent)
OnMouseOut func()
OnTapped func(e *fyne.PointEvent)
}
type HSpace struct { type HSpace struct {
widget.BaseWidget widget.BaseWidget
+51 -19
View File
@@ -1,12 +1,14 @@
package widgets package widgets
import ( import (
"image/color"
"sync" "sync"
"time" "time"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
@@ -72,6 +74,7 @@ func (g *FocusList) FocusNeighbor(curItem widget.ListItemID, up bool) {
var _ fyne.Tappable = (*FocusListRowBase)(nil) var _ fyne.Tappable = (*FocusListRowBase)(nil)
var _ fyne.Widget = (*FocusListRowBase)(nil) var _ fyne.Widget = (*FocusListRowBase)(nil)
var _ fyne.Focusable = (*FocusListRowBase)(nil) var _ fyne.Focusable = (*FocusListRowBase)(nil)
var _ desktop.Hoverable = (*FocusListRowBase)(nil)
// Base type used for all list rows in widgets such as Tracklist, etc. // Base type used for all list rows in widgets such as Tracklist, etc.
type FocusListRowBase struct { type FocusListRowBase struct {
@@ -81,14 +84,16 @@ type FocusListRowBase struct {
Content fyne.CanvasObject Content fyne.CanvasObject
Selected bool Selected bool
Focused bool Focused bool
hovered bool
OnTapped func() OnTapped func()
OnDoubleTapped func() OnDoubleTapped func()
OnFocusNeighbor func(up bool) //TODO: func(up, selecting bool) OnFocusNeighbor func(up bool) //TODO: func(up, selecting bool)
tappedAt int64 // unixMillis tappedAt int64 // unixMillis
focusedRect *canvas.Rectangle focusedRect canvas.Rectangle
selectionRect *canvas.Rectangle selectionRect canvas.Rectangle
hoverRect canvas.Rectangle
} }
func (l *FocusListRowBase) SetOnTapped(f func()) { func (l *FocusListRowBase) SetOnTapped(f func()) {
@@ -139,12 +144,14 @@ func (l *FocusListRowBase) Tapped(*fyne.PointEvent) {
func (l *FocusListRowBase) FocusGained() { func (l *FocusListRowBase) FocusGained() {
l.Focused = true l.Focused = true
l.Refresh() l.focusedRect.FillColor = theme.HoverColor()
l.focusedRect.Refresh()
} }
func (l *FocusListRowBase) FocusLost() { func (l *FocusListRowBase) FocusLost() {
l.Focused = false l.Focused = false
l.Refresh() l.focusedRect.FillColor = color.Transparent
l.focusedRect.Refresh()
} }
func (l *FocusListRowBase) TypedKey(e *fyne.KeyEvent) { func (l *FocusListRowBase) TypedKey(e *fyne.KeyEvent) {
@@ -180,26 +187,51 @@ func (l *FocusListRowBase) TypedKey(e *fyne.KeyEvent) {
func (l *FocusListRowBase) TypedRune(r rune) { func (l *FocusListRowBase) TypedRune(r rune) {
} }
func (l *FocusListRowBase) MouseIn(e *desktop.MouseEvent) {
l.hovered = true
l.hoverRect.FillColor = theme.HoverColor()
l.hoverRect.Refresh()
}
func (l *FocusListRowBase) MouseMoved(e *desktop.MouseEvent) {
}
func (l *FocusListRowBase) MouseOut() {
l.hovered = false
l.hoverRect.FillColor = color.Transparent
l.hoverRect.Refresh()
}
func (l *FocusListRowBase) Refresh() { func (l *FocusListRowBase) Refresh() {
l.focusedRect.FillColor = theme.HoverColor() l.updateBackgroundRendering()
l.focusedRect.Hidden = !l.Focused
l.selectionRect.FillColor = theme.SelectionColor()
l.selectionRect.Hidden = !l.Selected
l.BaseWidget.Refresh() l.BaseWidget.Refresh()
} }
func (l *FocusListRowBase) updateBackgroundRendering() {
if l.Selected {
l.selectionRect.FillColor = theme.SelectionColor()
} else {
l.selectionRect.FillColor = color.Transparent
}
if l.Focused {
l.focusedRect.FillColor = theme.HoverColor()
} else {
l.focusedRect.FillColor = color.Transparent
}
if l.hovered {
l.hoverRect.FillColor = theme.HoverColor()
} else {
l.hoverRect.FillColor = color.Transparent
}
}
func (l *FocusListRowBase) CreateRenderer() fyne.WidgetRenderer { func (l *FocusListRowBase) CreateRenderer() fyne.WidgetRenderer {
if l.selectionRect == nil { l.selectionRect.CornerRadius = theme.SelectionRadiusSize()
l.selectionRect = canvas.NewRectangle(theme.SelectionColor()) l.focusedRect.CornerRadius = theme.SelectionRadiusSize()
l.selectionRect.CornerRadius = theme.SelectionRadiusSize() l.hoverRect.CornerRadius = theme.SelectionRadiusSize()
l.selectionRect.Hidden = !l.Selected l.updateBackgroundRendering()
}
if l.focusedRect == nil {
l.focusedRect = canvas.NewRectangle(theme.HoverColor())
l.focusedRect.CornerRadius = theme.SelectionRadiusSize()
l.focusedRect.Hidden = !l.Focused
}
return widget.NewSimpleRenderer( return widget.NewSimpleRenderer(
container.NewStack(l.selectionRect, l.focusedRect, l.Content), container.NewStack(&l.selectionRect, &l.focusedRect, &l.hoverRect, l.Content),
) )
} }
+4 -2
View File
@@ -13,6 +13,7 @@ import (
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
"github.com/dweymouth/supersonic/res" "github.com/dweymouth/supersonic/res"
myTheme "github.com/dweymouth/supersonic/ui/theme" myTheme "github.com/dweymouth/supersonic/ui/theme"
"github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/util"
@@ -142,7 +143,7 @@ type GridViewItem struct {
itemID string itemID string
secondaryIDs []string secondaryIDs []string
primaryText *widget.Hyperlink primaryText *ttwidget.Hyperlink
secondaryText *MultiHyperlink secondaryText *MultiHyperlink
suffix string suffix string
container *fyne.Container container *fyne.Container
@@ -165,7 +166,7 @@ type GridViewItem struct {
func NewGridViewItem(placeholderResource fyne.Resource) *GridViewItem { func NewGridViewItem(placeholderResource fyne.Resource) *GridViewItem {
g := &GridViewItem{ g := &GridViewItem{
primaryText: widget.NewHyperlink("", nil), primaryText: ttwidget.NewHyperlink("", nil),
secondaryText: NewMultiHyperlink(), secondaryText: NewMultiHyperlink(),
Cover: newCoverImage(placeholderResource), Cover: newCoverImage(placeholderResource),
} }
@@ -220,6 +221,7 @@ func (g *GridViewItem) Update(model GridViewItemModel) {
g.itemID = model.ID g.itemID = model.ID
g.secondaryIDs = model.SecondaryIDs g.secondaryIDs = model.SecondaryIDs
g.primaryText.SetText(model.Name) g.primaryText.SetText(model.Name)
g.primaryText.SetToolTip(model.Name)
g.secondaryText.BuildSegments(model.Secondary, model.SecondaryIDs) g.secondaryText.BuildSegments(model.Secondary, model.SecondaryIDs)
if g.ShowSuffix { if g.ShowSuffix {
g.secondaryText.Suffix = model.Suffix g.secondaryText.Suffix = model.Suffix
+31 -7
View File
@@ -3,8 +3,10 @@ package widgets
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
) )
type MultiHyperlink struct { type MultiHyperlink struct {
@@ -16,7 +18,9 @@ type MultiHyperlink struct {
// only if there is enough room // only if there is enough room
Suffix string Suffix string
OnTapped func(string) OnMouseIn func(*desktop.MouseEvent)
OnMouseOut func()
OnTapped func(string)
SizeName fyne.ThemeSizeName SizeName fyne.ThemeSizeName
SuffixSizeName fyne.ThemeSizeName SuffixSizeName fyne.ThemeSizeName
@@ -30,7 +34,7 @@ type MultiHyperlink struct {
//provider *widget.RichText //provider *widget.RichText
objects []fyne.CanvasObject objects []fyne.CanvasObject
suffixLabel *widget.RichText suffixLabel *ttwidget.RichText
content *fyne.Container content *fyne.Container
} }
@@ -136,7 +140,9 @@ func (c *MultiHyperlink) layoutObjects() {
c.content.Objects = c.objects[:2*i-1] c.content.Objects = c.objects[:2*i-1]
if i == len(c.Segments) && c.Suffix != "" { if i == len(c.Segments) && c.Suffix != "" {
if c.suffixLabel == nil { if c.suffixLabel == nil {
c.suffixLabel = widget.NewRichTextWithText("· " + c.Suffix) c.suffixLabel = ttwidget.NewRichTextWithText("· " + c.Suffix)
c.suffixLabel.OnMouseIn = c.callOnMouseIn
c.suffixLabel.OnMouseOut = c.callOnMouseOut
} else { } else {
c.suffixLabel.Segments[0].(*widget.TextSegment).Text = "· " + c.Suffix c.suffixLabel.Segments[0].(*widget.TextSegment).Text = "· " + c.Suffix
} }
@@ -157,14 +163,18 @@ func (c *MultiHyperlink) layoutObjects() {
func (c *MultiHyperlink) updateOrReplaceLabel(obj fyne.CanvasObject, text string) fyne.CanvasObject { func (c *MultiHyperlink) updateOrReplaceLabel(obj fyne.CanvasObject, text string) fyne.CanvasObject {
if obj != nil { if obj != nil {
if label, ok := obj.(*widget.RichText); ok { if label, ok := obj.(*ttwidget.RichText); ok {
ts := label.Segments[0].(*widget.TextSegment) ts := label.Segments[0].(*widget.TextSegment)
ts.Text = text ts.Text = text
ts.Style.SizeName = c.sizeName() ts.Style.SizeName = c.sizeName()
label.SetToolTip(text)
return label return label
} }
} }
l := widget.NewRichTextWithText(text) l := ttwidget.NewRichTextWithText(text)
l.SetToolTip(text)
l.OnMouseIn = c.callOnMouseIn
l.OnMouseOut = c.callOnMouseOut
l.Segments[0].(*widget.TextSegment).Style.SizeName = c.sizeName() l.Segments[0].(*widget.TextSegment).Style.SizeName = c.sizeName()
l.Truncation = fyne.TextTruncateEllipsis l.Truncation = fyne.TextTruncateEllipsis
return l return l
@@ -172,20 +182,34 @@ func (c *MultiHyperlink) updateOrReplaceLabel(obj fyne.CanvasObject, text string
func (c *MultiHyperlink) updateOrReplaceHyperlink(obj fyne.CanvasObject, text, link string) fyne.CanvasObject { func (c *MultiHyperlink) updateOrReplaceHyperlink(obj fyne.CanvasObject, text, link string) fyne.CanvasObject {
if obj != nil { if obj != nil {
if l, ok := obj.(*widget.Hyperlink); ok { if l, ok := obj.(*ttwidget.Hyperlink); ok {
l.Text = text l.Text = text
l.SetToolTip(text)
l.SizeName = c.sizeName() l.SizeName = c.sizeName()
l.OnTapped = func() { c.onSegmentTapped(link) } l.OnTapped = func() { c.onSegmentTapped(link) }
return l return l
} }
} }
l := widget.NewHyperlink(text, nil) l := ttwidget.NewHyperlink(text, nil)
l.SetToolTip(text)
l.SizeName = c.sizeName() l.SizeName = c.sizeName()
l.Truncation = fyne.TextTruncateEllipsis l.Truncation = fyne.TextTruncateEllipsis
l.OnTapped = func() { c.onSegmentTapped(link) } l.OnTapped = func() { c.onSegmentTapped(link) }
return l return l
} }
func (c *MultiHyperlink) callOnMouseIn(e *desktop.MouseEvent) {
if f := c.OnMouseIn; f != nil {
f(e)
}
}
func (c *MultiHyperlink) callOnMouseOut() {
if f := c.OnMouseOut; f != nil {
f()
}
}
func (c *MultiHyperlink) newSeparatorLabel() *widget.RichText { func (c *MultiHyperlink) newSeparatorLabel() *widget.RichText {
rt := widget.NewRichTextWithText(", ") rt := widget.NewRichTextWithText(", ")
rt.Segments[0].(*widget.TextSegment).Style.SizeName = c.sizeName() rt.Segments[0].(*widget.TextSegment).Style.SizeName = c.sizeName()
+8 -4
View File
@@ -15,6 +15,7 @@ import (
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
) )
// Shows the current album art, track name, artist name, and album name // Shows the current album art, track name, artist name, and album name
@@ -26,7 +27,7 @@ type NowPlayingCard struct {
trackName *OptionHyperlink trackName *OptionHyperlink
artistName *MultiHyperlink artistName *MultiHyperlink
albumName *widget.Hyperlink albumName *ttwidget.Hyperlink
cover *ImagePlaceholder cover *ImagePlaceholder
menu *widget.PopUpMenu menu *widget.PopUpMenu
ratingMenu *fyne.MenuItem ratingMenu *fyne.MenuItem
@@ -46,7 +47,7 @@ func NewNowPlayingCard() *NowPlayingCard {
n := &NowPlayingCard{ n := &NowPlayingCard{
trackName: NewOptionHyperlink(), trackName: NewOptionHyperlink(),
artistName: NewMultiHyperlink(), artistName: NewMultiHyperlink(),
albumName: widget.NewHyperlink("", nil), albumName: ttwidget.NewHyperlink("", nil),
} }
n.ExtendBaseWidget(n) n.ExtendBaseWidget(n)
n.cover = NewImagePlaceholder(myTheme.TracksIcon, 85) n.cover = NewImagePlaceholder(myTheme.TracksIcon, 85)
@@ -135,20 +136,23 @@ func (n *NowPlayingCard) CreateRenderer() fyne.WidgetRenderer {
func (n *NowPlayingCard) Update(track mediaprovider.MediaItem) { func (n *NowPlayingCard) Update(track mediaprovider.MediaItem) {
if track == nil { if track == nil {
n.trackName.SetText("") n.trackName.SetTextAndToolTip("")
n.artistName.BuildSegments([]string{}, []string{}) n.artistName.BuildSegments([]string{}, []string{})
n.albumName.SetText("") n.albumName.SetText("")
n.albumName.SetToolTip("")
n.cover.Hidden = true n.cover.Hidden = true
} else { } else {
n.cover.Hidden = false n.cover.Hidden = false
n.trackName.SetText(track.Metadata().Name) n.trackName.SetTextAndToolTip(track.Metadata().Name)
if tr, ok := track.(*mediaprovider.Track); ok { if tr, ok := track.(*mediaprovider.Track); ok {
n.artistName.BuildSegments(tr.ArtistNames, tr.ArtistIDs) n.artistName.BuildSegments(tr.ArtistNames, tr.ArtistIDs)
n.albumName.SetText(tr.Album) n.albumName.SetText(tr.Album)
n.albumName.SetToolTip(tr.Album)
n.cover.PlaceholderIcon = myTheme.TracksIcon n.cover.PlaceholderIcon = myTheme.TracksIcon
} else { } else {
n.artistName.BuildSegments([]string{}, []string{}) n.artistName.BuildSegments([]string{}, []string{})
n.albumName.SetText("") n.albumName.SetText("")
n.albumName.SetToolTip("")
n.cover.PlaceholderIcon = myTheme.RadioIcon n.cover.PlaceholderIcon = myTheme.RadioIcon
} }
} }
+5 -3
View File
@@ -5,13 +5,14 @@ import (
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
) )
// OptionHyperlink is a widget that displays a "vertical dots"/"more" button // OptionHyperlink is a widget that displays a "vertical dots"/"more" button
// to the right of the text, which can show a menu. // to the right of the text, which can show a menu.
type OptionHyperlink struct { type OptionHyperlink struct {
widget.BaseWidget widget.BaseWidget
h *widget.Hyperlink h *ttwidget.Hyperlink
b *IconButton b *IconButton
OnShowMenu func(btnPos fyne.Position) OnShowMenu func(btnPos fyne.Position)
@@ -21,7 +22,7 @@ type OptionHyperlink struct {
func NewOptionHyperlink() *OptionHyperlink { func NewOptionHyperlink() *OptionHyperlink {
o := &OptionHyperlink{ o := &OptionHyperlink{
h: widget.NewHyperlink("", nil), h: ttwidget.NewHyperlink("", nil),
} }
o.h.Truncation = fyne.TextTruncateEllipsis o.h.Truncation = fyne.TextTruncateEllipsis
o.b = NewIconButton(theme.MoreVerticalIcon(), func() { o.b = NewIconButton(theme.MoreVerticalIcon(), func() {
@@ -34,8 +35,9 @@ func NewOptionHyperlink() *OptionHyperlink {
return o return o
} }
func (o *OptionHyperlink) SetText(text string) { func (o *OptionHyperlink) SetTextAndToolTip(text string) {
o.h.SetText(text) o.h.SetText(text)
o.h.SetToolTip(text)
o.updatePreferredWidth() o.updatePreferredWidth()
o.BaseWidget.Refresh() o.BaseWidget.Refresh()
} }
+13 -10
View File
@@ -15,6 +15,7 @@ import (
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
"github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/sharedutil" "github.com/dweymouth/supersonic/sharedutil"
@@ -436,7 +437,7 @@ type PlayQueueListRow struct {
playingIcon fyne.CanvasObject playingIcon fyne.CanvasObject
num *widget.Label num *widget.Label
cover *ImagePlaceholder cover *ImagePlaceholder
title *widget.Label title *ttwidget.Label
artist *MultiHyperlink artist *MultiHyperlink
time *widget.Label time *widget.Label
} }
@@ -447,14 +448,18 @@ func NewPlayQueueListRow(playQueueList *PlayQueueList, im *backend.ImageManager,
playQueueList: playQueueList, playQueueList: playQueueList,
num: widget.NewLabel(""), num: widget.NewLabel(""),
cover: NewImagePlaceholder(myTheme.TracksIcon, playQueueListThumbnailSize), cover: NewImagePlaceholder(myTheme.TracksIcon, playQueueListThumbnailSize),
title: util.NewTruncatingLabel(), title: util.NewTruncatingTooltipLabel(),
artist: NewMultiHyperlink(), artist: NewMultiHyperlink(),
time: util.NewTrailingAlignLabel(), time: util.NewTrailingAlignLabel(),
} }
p.ExtendBaseWidget(p) p.ExtendBaseWidget(p)
p.cover.ScaleMode = canvas.ImageScaleFastest p.cover.ScaleMode = canvas.ImageScaleFastest
p.title.OnMouseIn = p.MouseIn
p.title.OnMouseOut = p.MouseOut
p.artist.OnTapped = playQueueList.onArtistTapped p.artist.OnTapped = playQueueList.onArtistTapped
p.artist.OnMouseIn = p.MouseIn
p.artist.OnMouseOut = p.MouseOut
p.OnDoubleTapped = func() { p.OnDoubleTapped = func() {
playQueueList.onPlayTrackAt(p.ItemID()) playQueueList.onPlayTrackAt(p.ItemID())
} }
@@ -490,15 +495,12 @@ func (p *PlayQueueListRow) TappedSecondary(e *fyne.PointEvent) {
} }
func (p *PlayQueueListRow) Update(tm *util.TrackListModel, rowNum int) { func (p *PlayQueueListRow) Update(tm *util.TrackListModel, rowNum int) {
changed := false
if tm.Selected != p.Selected { if tm.Selected != p.Selected {
p.Selected = tm.Selected p.Selected = tm.Selected
changed = true
} }
if num := strconv.Itoa(rowNum); p.num.Text != num { if num := strconv.Itoa(rowNum); p.num.Text != num {
p.num.Text = num p.num.Text = num
changed = true
} }
// Update info that can change if this row is bound to // Update info that can change if this row is bound to
@@ -514,9 +516,9 @@ func (p *PlayQueueListRow) Update(tm *util.TrackListModel, rowNum int) {
p.EnsureUnfocused() p.EnsureUnfocused()
p.trackID = meta.ID p.trackID = meta.ID
p.title.Text = meta.Name p.title.Text = meta.Name
p.title.SetToolTip(meta.Name)
p.artist.BuildSegments(meta.Artists, meta.ArtistIDs) p.artist.BuildSegments(meta.Artists, meta.ArtistIDs)
p.time.Text = util.SecondsToMMSS(float64(meta.Duration)) p.time.Text = util.SecondsToMMSS(float64(meta.Duration))
changed = true
} }
// Render whether track is playing or not // Render whether track is playing or not
@@ -529,10 +531,11 @@ func (p *PlayQueueListRow) Update(tm *util.TrackListModel, rowNum int) {
} else { } else {
p.Content.(*fyne.Container).Objects[0] = container.NewCenter(p.num) p.Content.(*fyne.Container).Objects[0] = container.NewCenter(p.num)
} }
changed = true
} }
if changed { // we always need to refresh in case of light/dark change
p.Refresh() // even if no info changed in the update, since the
} // PlayQueueList is used in the pop up queue which may be
// hidden and re-shown after a theme variant change
p.Refresh()
} }
+22 -6
View File
@@ -13,6 +13,7 @@ import (
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
"github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend"
myTheme "github.com/dweymouth/supersonic/ui/theme" myTheme "github.com/dweymouth/supersonic/ui/theme"
"github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/util"
@@ -151,7 +152,7 @@ type tracklistRowBase struct {
playCount int playCount int
num *widget.Label num *widget.Label
name *widget.RichText // for bold support name *ttwidget.RichText
artist *MultiHyperlink artist *MultiHyperlink
album *MultiHyperlink // for disabled support, if albumID is "" album *MultiHyperlink // for disabled support, if albumID is ""
composer *MultiHyperlink composer *MultiHyperlink
@@ -161,9 +162,9 @@ type tracklistRowBase struct {
rating *StarRating rating *StarRating
bitrate *widget.Label bitrate *widget.Label
plays *widget.Label plays *widget.Label
comment *widget.Label comment *ttwidget.Label
size *widget.Label size *widget.Label
path *widget.Label path *ttwidget.Label
// must be injected by extending widget // must be injected by extending widget
setColVisibility func(int, bool) bool setColVisibility func(int, bool) bool
@@ -274,13 +275,21 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject)
func (t *tracklistRowBase) create(tracklist *Tracklist) { func (t *tracklistRowBase) create(tracklist *Tracklist) {
t.tracklist = tracklist t.tracklist = tracklist
t.num = util.NewTrailingAlignLabel() t.num = util.NewTrailingAlignLabel()
t.name = util.NewTruncatingRichText() t.name = util.NewTruncatingTooltipRichText()
t.name.OnMouseIn = t.MouseIn
t.name.OnMouseOut = t.MouseOut
t.artist = NewMultiHyperlink() t.artist = NewMultiHyperlink()
t.artist.OnTapped = tracklist.onArtistTapped t.artist.OnTapped = tracklist.onArtistTapped
t.artist.OnMouseIn = t.MouseIn
t.artist.OnMouseOut = t.MouseOut
t.album = NewMultiHyperlink() t.album = NewMultiHyperlink()
t.album.OnTapped = func(id string) { tracklist.onAlbumTapped(id) } t.album.OnTapped = func(id string) { tracklist.onAlbumTapped(id) }
t.album.OnMouseIn = t.MouseIn
t.album.OnMouseOut = t.MouseOut
t.composer = NewMultiHyperlink() t.composer = NewMultiHyperlink()
t.composer.OnTapped = func(id string) { tracklist.onArtistTapped(id) } t.composer.OnTapped = func(id string) { tracklist.onArtistTapped(id) }
t.composer.OnMouseIn = t.MouseIn
t.composer.OnMouseOut = t.MouseOut
t.dur = util.NewTrailingAlignLabel() t.dur = util.NewTrailingAlignLabel()
t.year = util.NewTrailingAlignLabel() t.year = util.NewTrailingAlignLabel()
favorite := NewFavoriteIcon() favorite := NewFavoriteIcon()
@@ -291,10 +300,14 @@ func (t *tracklistRowBase) create(tracklist *Tracklist) {
t.rating.StarSize = 16 t.rating.StarSize = 16
t.rating.OnRatingChanged = t.setTrackRating t.rating.OnRatingChanged = t.setTrackRating
t.plays = util.NewTrailingAlignLabel() t.plays = util.NewTrailingAlignLabel()
t.comment = util.NewTruncatingLabel() t.comment = util.NewTruncatingTooltipLabel()
t.comment.OnMouseIn = t.MouseIn
t.comment.OnMouseOut = t.MouseOut
t.bitrate = util.NewTrailingAlignLabel() t.bitrate = util.NewTrailingAlignLabel()
t.size = util.NewTrailingAlignLabel() t.size = util.NewTrailingAlignLabel()
t.path = util.NewTruncatingLabel() t.path = util.NewTruncatingTooltipLabel()
t.path.OnMouseIn = t.MouseIn
t.path.OnMouseOut = t.MouseOut
} }
func (t *tracklistRowBase) SetOnTappedSecondary(f func(*fyne.PointEvent, int)) { func (t *tracklistRowBase) SetOnTappedSecondary(f func(*fyne.PointEvent, int)) {
@@ -319,6 +332,7 @@ func (t *tracklistRowBase) Update(tm *util.TrackListModel, rowNum int) {
t.trackID = id t.trackID = id
t.name.Segments[0].(*widget.TextSegment).Text = tr.Title t.name.Segments[0].(*widget.TextSegment).Text = tr.Title
t.name.SetToolTip(tr.Title)
t.artist.BuildSegments(tr.ArtistNames, tr.ArtistIDs) t.artist.BuildSegments(tr.ArtistNames, tr.ArtistIDs)
t.album.BuildSegments([]string{tr.Album}, []string{tr.AlbumID}) t.album.BuildSegments([]string{tr.Album}, []string{tr.AlbumID})
t.composer.BuildSegments(tr.ComposerNames, tr.ComposerIDs) t.composer.BuildSegments(tr.ComposerNames, tr.ComposerIDs)
@@ -326,9 +340,11 @@ func (t *tracklistRowBase) Update(tm *util.TrackListModel, rowNum int) {
t.year.Text = strconv.Itoa(tr.Year) t.year.Text = strconv.Itoa(tr.Year)
t.plays.Text = strconv.Itoa(int(tr.PlayCount)) t.plays.Text = strconv.Itoa(int(tr.PlayCount))
t.comment.Text = tr.Comment t.comment.Text = tr.Comment
t.comment.SetToolTip(tr.Comment)
t.bitrate.Text = strconv.Itoa(tr.BitRate) t.bitrate.Text = strconv.Itoa(tr.BitRate)
t.size.Text = util.BytesToSizeString(tr.Size) t.size.Text = util.BytesToSizeString(tr.Size)
t.path.Text = tr.FilePath t.path.Text = tr.FilePath
t.path.SetToolTip(tr.FilePath)
changed = true changed = true
} }