package ui import ( "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/player/mpv" "github.com/dweymouth/supersonic/ui/controller" "github.com/dweymouth/supersonic/ui/layouts" "github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/widgets" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/widget" ) type BottomPanel struct { widget.BaseWidget imageLoader util.ThumbnailLoader NowPlaying *widgets.NowPlayingCard Controls *widgets.PlayerControls AuxControls *widgets.AuxControls container *fyne.Container cfg *backend.Config } var _ fyne.Widget = (*BottomPanel)(nil) func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr *controller.Controller, cfg *backend.Config) *BottomPanel { bp := &BottomPanel{cfg: cfg} bp.ExtendBaseWidget(bp) pm.OnSongChange(bp.onSongChange) pm.OnRadioMetadataChange(bp.onRadioMetadataChange) pm.OnWaveformImgUpdate(bp.updateWaveformImg) pm.OnPlayTimeUpdate(func(cur, total float64, _ bool) { fyne.Do(func() { if !pm.IsSeeking() { bp.Controls.UpdatePlayTime(cur, total) } }) }) pm.OnPaused(util.FyneDoFunc(func() { bp.Controls.SetPlaying(false) })) pm.OnPlaying(util.FyneDoFunc(func() { bp.Controls.SetPlaying(true) })) pm.OnStopped(util.FyneDoFunc(func() { bp.Controls.SetPlaying(false) bp.Controls.UpdatePlayTime(0, 0) })) bp.NowPlaying = widgets.NewNowPlayingCard() _, canShare := contr.App.ServerManager.Server.(mediaprovider.SupportsSharing) bp.NowPlaying.DisableSharing = !canShare bp.NowPlaying.ShowAlbumYear = cfg.AlbumsPage.ShowYears bp.NowPlaying.OnCoverTapped = func() { contr.NavigateTo(controller.NowPlayingRoute()) } bp.NowPlaying.OnSetFavorite = func(fav bool) { if tr, ok := pm.NowPlaying().(*mediaprovider.Track); ok { contr.SetTrackFavorites([]string{tr.ID}, fav) } } bp.NowPlaying.OnSetRating = func(rating int) { if tr, ok := pm.NowPlaying().(*mediaprovider.Track); ok { contr.SetTrackRatings([]string{tr.ID}, rating) } } bp.NowPlaying.OnAddToPlaylist = func() { if tr, ok := pm.NowPlaying().(*mediaprovider.Track); ok { contr.DoAddTracksToPlaylistWorkflow([]string{tr.ID}) } } bp.NowPlaying.OnAlbumNameTapped = func(albumID string) { contr.NavigateTo(controller.AlbumRoute(albumID)) } bp.NowPlaying.OnArtistNameTapped = func(artistID string) { contr.NavigateTo(controller.ArtistRoute(artistID)) } bp.NowPlaying.OnTrackNameTapped = func() { contr.NavigateTo(controller.NowPlayingRoute()) } bp.NowPlaying.OnShowTrackInfo = func() { if tr, ok := pm.NowPlaying().(*mediaprovider.Track); ok { contr.ShowTrackInfoDialog(tr) } } bp.NowPlaying.OnShare = func() { switch item := pm.NowPlaying().(type) { case *mediaprovider.Track: contr.ShowShareDialog(item.ID) case *mediaprovider.PodcastEpisode: if item.StreamID != "" { contr.ShowShareDialog(item.StreamID) } } } bp.Controls = widgets.NewPlayerControls(cfg.Playback.UseWaveformSeekbar, pm.GetLoopMode(), pm.IsShuffle()) bp.Controls.OnPlayPause(func() { pm.PlayPause() }) bp.Controls.OnSeekNext(func() { pm.SeekNext() }) bp.Controls.OnSeekPrevious(func() { pm.SeekBackOrPrevious() }) bp.Controls.OnSeek(func(f float64) { pm.SeekFraction(f) }) bp.Controls.OnChangeLoopMode(func() { pm.SetNextLoopMode() }) bp.Controls.OnChangeShuffle = func(shuffle bool) { pm.SetShuffle(shuffle) } pm.OnLoopModeChange(func(lm backend.LoopMode) { fyne.Do(func() { bp.Controls.SetLoopMode(lm) }) }) pm.OnShuffleChange(func(sh bool) { fyne.Do(func() { bp.Controls.SetShuffle(sh) }) }) bp.AuxControls = widgets.NewAuxControls(pm.Volume(), pm.IsAutoplay()) pm.OnVolumeChange(func(vol int) { fyne.Do(func() { bp.AuxControls.VolumeControl.SetVolume(vol) }) }) pm.OnPlayerChange(func() { _, local := pm.CurrentPlayer().(*mpv.Player) fyne.Do(func() { bp.AuxControls.SetIsRemotePlayer(!local) }) }) bp.AuxControls.VolumeControl.OnSetVolume = func(v int) { pm.SetVolume(v) } bp.AuxControls.OnChangeAutoplay = func(autoplay bool) { pm.SetAutoplay(autoplay) } bp.AuxControls.OnShowPlayQueue(contr.ShowPopUpPlayQueue) bp.AuxControls.OnShowCastMenu(contr.ShowCastMenu) bp.imageLoader = util.NewThumbnailLoader(im, bp.NowPlaying.SetImage) bp.container = container.New(layouts.NewLeftMiddleRightLayout(300, 0.4), bp.NowPlaying, bp.Controls, bp.AuxControls) return bp } func (bp *BottomPanel) onSongChange(song mediaprovider.MediaItem, _ *mediaprovider.Track) { fyne.Do(func() { if song == nil { bp.NowPlaying.Update(nil) bp.imageLoader.Load("") } else { bp.NowPlaying.Update(song) bp.imageLoader.Load(song.Metadata().CoverArtID) } }) } func (bp *BottomPanel) onRadioMetadataChange(radioName, title, artist string) { fyne.Do(func() { bp.NowPlaying.Update(&mediaprovider.Track{ Title: title, ArtistNames: []string{artist}, Album: radioName, }) }) } func (bp *BottomPanel) updateWaveformImg(img *backend.WaveformImage) { fyne.Do(func() { bp.Controls.UpdateWaveformImg(img) }) } func (bp *BottomPanel) Refresh() { bp.NowPlaying.ShowAlbumYear = bp.cfg.AlbumsPage.ShowYears bp.BaseWidget.Refresh() } func (bp *BottomPanel) CreateRenderer() fyne.WidgetRenderer { bp.ExtendBaseWidget(bp) return widget.NewSimpleRenderer(bp.container) }