Merge branch 'main' into new-add-to-playlist
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ func NewBottomPanel(pm *backend.PlaybackManager, contr *controller.Controller) *
|
||||
bp.ExtendBaseWidget(bp)
|
||||
|
||||
pm.OnSongChange(bp.onSongChange)
|
||||
pm.OnPlayTimeUpdate(func(cur, total float64) {
|
||||
pm.OnPlayTimeUpdate(func(cur, total float64, _ bool) {
|
||||
if !pm.IsSeeking() {
|
||||
bp.Controls.UpdatePlayTime(cur, total)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ type CanShowNowPlaying interface {
|
||||
}
|
||||
|
||||
type CanShowPlayTime interface {
|
||||
OnPlayTimeUpdate(curTime, totalTime float64)
|
||||
OnPlayTimeUpdate(curTime, totalTime float64, seeked bool)
|
||||
}
|
||||
|
||||
type CanShowPlayQueue interface {
|
||||
@@ -222,12 +222,12 @@ func (b *BrowsingPane) onSongChange(song, lastScrobbledIfAny *mediaprovider.Trac
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BrowsingPane) onPlayTimeUpdate(cur, total float64) {
|
||||
func (b *BrowsingPane) onPlayTimeUpdate(cur, total float64, seeked bool) {
|
||||
if b.curPage == nil {
|
||||
return
|
||||
}
|
||||
if p, ok := b.curPage.(CanShowPlayTime); ok {
|
||||
p.OnPlayTimeUpdate(cur, total)
|
||||
p.OnPlayTimeUpdate(cur, total, seeked)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,8 +241,13 @@ func (s *nowPlayingPageState) Restore() Page {
|
||||
|
||||
var _ CanShowPlayTime = (*NowPlayingPage)(nil)
|
||||
|
||||
func (a *NowPlayingPage) OnPlayTimeUpdate(_, _ float64) {
|
||||
func (a *NowPlayingPage) OnPlayTimeUpdate(curTime, _ float64, seeked bool) {
|
||||
a.formatStatusLine()
|
||||
if seeked {
|
||||
a.lyricsViewer.OnSeeked(curTime)
|
||||
} else {
|
||||
a.lyricsViewer.UpdatePlayPos(curTime)
|
||||
}
|
||||
}
|
||||
|
||||
var _ CanSelectAll = (*NowPlayingPage)(nil)
|
||||
|
||||
+74
-31
@@ -4,66 +4,109 @@ import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
fynelyrics "github.com/dweymouth/fyne-lyrics"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
type LyricsViewer struct {
|
||||
widget.BaseWidget
|
||||
|
||||
noLyricsLabel widget.Label
|
||||
unsyncedViewer *widget.RichText
|
||||
noLyricsLabel widget.Label
|
||||
viewer *fynelyrics.LyricsViewer
|
||||
lyrics *mediaprovider.Lyrics
|
||||
nextLyricLine int
|
||||
lastPlayPos float64
|
||||
|
||||
container *container.Scroll
|
||||
currentView lyricView
|
||||
// keeps track if UpdatePlayPos has been called yet
|
||||
// for the current lyrics
|
||||
firstUpdate bool
|
||||
|
||||
container *fyne.Container
|
||||
isEmpty bool
|
||||
}
|
||||
|
||||
type lyricView int
|
||||
|
||||
const (
|
||||
lyricViewEmpty lyricView = iota
|
||||
lyricViewUnsynced
|
||||
lyricViewSynced
|
||||
)
|
||||
|
||||
func NewLyricsViewer() *LyricsViewer {
|
||||
l := &LyricsViewer{noLyricsLabel: widget.Label{
|
||||
Text: "Lyrics not available",
|
||||
}}
|
||||
}, isEmpty: true}
|
||||
l.ExtendBaseWidget(l)
|
||||
l.container = container.NewVScroll(&l.noLyricsLabel)
|
||||
l.container = container.NewStack(&l.noLyricsLabel)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LyricsViewer) SetLyrics(lyrics *mediaprovider.Lyrics) {
|
||||
l.lyrics = lyrics
|
||||
l.nextLyricLine = 0
|
||||
l.firstUpdate = true
|
||||
if lyrics == nil || len(lyrics.Lines) == 0 {
|
||||
if l.currentView != lyricViewEmpty {
|
||||
l.container.Content = &l.noLyricsLabel
|
||||
l.currentView = lyricViewEmpty
|
||||
if !l.isEmpty {
|
||||
l.container.Objects[0] = &l.noLyricsLabel
|
||||
l.isEmpty = true
|
||||
l.Refresh()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if l.unsyncedViewer == nil {
|
||||
l.unsyncedViewer = widget.NewRichText()
|
||||
l.unsyncedViewer.Wrapping = fyne.TextWrapWord
|
||||
if l.viewer == nil {
|
||||
l.viewer = fynelyrics.NewLyricsViewer()
|
||||
l.viewer.ActiveLyricPosition = fynelyrics.ActiveLyricPositionTopThird
|
||||
}
|
||||
l.unsyncedViewer.Segments = nil
|
||||
for _, line := range lyrics.Lines {
|
||||
ts := &widget.TextSegment{Text: line.Text}
|
||||
ts.Style.Alignment = fyne.TextAlignCenter
|
||||
ts.Style.SizeName = widget.RichTextStyleSubHeading.SizeName
|
||||
ts.Style.Inline = false
|
||||
l.unsyncedViewer.Segments = append(l.unsyncedViewer.Segments, ts)
|
||||
lines := make([]string, len(lyrics.Lines))
|
||||
for i, line := range lyrics.Lines {
|
||||
lines[i] = line.Text
|
||||
}
|
||||
l.unsyncedViewer.Refresh()
|
||||
if l.currentView != lyricViewUnsynced {
|
||||
l.container.Content = l.unsyncedViewer
|
||||
l.currentView = lyricViewUnsynced
|
||||
l.viewer.SetLyrics(lines, lyrics.Synced)
|
||||
if l.isEmpty {
|
||||
l.container.Objects[0] = l.viewer
|
||||
l.isEmpty = false
|
||||
l.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LyricsViewer) UpdatePlayPos(timeSecs float64) {
|
||||
if l.lyrics == nil || !l.lyrics.Synced {
|
||||
return
|
||||
}
|
||||
if l.firstUpdate || timeSecs < l.lastPlayPos {
|
||||
l.OnSeeked(timeSecs)
|
||||
l.firstUpdate = false
|
||||
return
|
||||
}
|
||||
l.lastPlayPos = timeSecs
|
||||
// advance if needed
|
||||
if l.lyrics.Lines[l.nextLyricLine].Start <= timeSecs {
|
||||
l.viewer.NextLine()
|
||||
if l.nextLyricLine < len(l.lyrics.Lines)-1 {
|
||||
l.nextLyricLine++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LyricsViewer) OnSeeked(timeSecs float64) {
|
||||
l.lastPlayPos = timeSecs
|
||||
if l.lyrics == nil || !l.lyrics.Synced {
|
||||
return
|
||||
}
|
||||
|
||||
// find first line that starts after timeSecs
|
||||
nextLine := -1
|
||||
for i, l := range l.lyrics.Lines {
|
||||
if l.Start > timeSecs {
|
||||
nextLine = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nextLine == -1 {
|
||||
// last lyric
|
||||
nextLine = len(l.lyrics.Lines)
|
||||
l.nextLyricLine = nextLine - 1
|
||||
} else {
|
||||
l.nextLyricLine = nextLine
|
||||
}
|
||||
l.viewer.SetCurrentLine(nextLine /*one-indexed*/)
|
||||
}
|
||||
|
||||
func (l *LyricsViewer) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(l.container)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user