diff --git a/ui/browsing/playlistspage.go b/ui/browsing/playlistspage.go new file mode 100644 index 0000000..594a1d9 --- /dev/null +++ b/ui/browsing/playlistspage.go @@ -0,0 +1,173 @@ +package browsing + +import ( + "log" + "strconv" + "supersonic/backend" + "supersonic/ui/layouts" + "supersonic/ui/widgets" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" + "github.com/dweymouth/go-subsonic" +) + +type PlaylistsPage struct { + widget.BaseWidget + + sm *backend.ServerManager + nav func(Route) + titleDisp *widget.RichText + container *fyne.Container + list *PlaylistList +} + +func NewPlaylistsPage(sm *backend.ServerManager, nav func(Route)) *PlaylistsPage { + a := &PlaylistsPage{ + sm: sm, + nav: nav, + titleDisp: widget.NewRichTextWithText("Playlists"), + } + a.ExtendBaseWidget(a) + a.titleDisp.Segments[0].(*widget.TextSegment).Style.SizeName = theme.SizeNameHeadingText + a.list = NewPlaylistList() + a.list.OnNavTo = func(id string) { + log.Println("opening playlist", id) + //nav(PlaylistRoute(id)) + } + a.buildContainer() + go a.loadAsync() + return a +} + +func (a *PlaylistsPage) loadAsync() { + playlists, err := a.sm.Server.GetPlaylists(nil) + if err != nil { + log.Printf("error loading playlists: %v", err.Error()) + } + a.list.Playlists = playlists + a.list.Refresh() +} + +func (a *PlaylistsPage) Route() Route { + return PlaylistsRoute() +} + +func (a *PlaylistsPage) Reload() { + go a.loadAsync() +} + +func (a *PlaylistsPage) Save() SavedPage { + return &savedPlaylistsPage{ + sm: a.sm, + nav: a.nav, + } +} + +type savedPlaylistsPage struct { + sm *backend.ServerManager + nav func(Route) +} + +func (s *savedPlaylistsPage) Restore() Page { + return NewPlaylistsPage(s.sm, s.nav) +} + +func (a *PlaylistsPage) buildContainer() { + a.container = container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15}, + container.NewBorder(a.titleDisp, nil, nil, nil, a.list)) +} + +func (a *PlaylistsPage) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(a.container) +} + +type PlaylistList struct { + widget.BaseWidget + + Playlists []*subsonic.Playlist + OnNavTo func(string) + + columnsLayout *layouts.ColumnsLayout + header *widgets.ListHeader + list *widget.List + container *fyne.Container +} + +func NewPlaylistList() *PlaylistList { + a := &PlaylistList{ + columnsLayout: layouts.NewColumnsLayout([]float32{-1, -1, 200, 125}), + } + a.header = widgets.NewListHeader([]string{"Name", "Description", "Owner", "Track Count"}, a.columnsLayout) + a.list = widget.NewList( + func() int { + return len(a.Playlists) + }, + func() fyne.CanvasObject { + r := NewPlaylistListRow(a.columnsLayout) + r.OnTapped = func() { a.onRowTapped(r.ID) } + return r + }, + func(id widget.ListItemID, item fyne.CanvasObject) { + row := item.(*PlaylistListRow) + row.ID = a.Playlists[id].ID + row.nameLabel.Text = a.Playlists[id].Name + row.descrptionLabel.Text = a.Playlists[id].Name + row.ownerLabel.Text = a.Playlists[id].Owner + row.trackCountLabel.Text = strconv.Itoa(a.Playlists[id].SongCount) + row.Refresh() + }, + ) + a.container = container.NewBorder(a.header, nil, nil, nil, a.list) + a.ExtendBaseWidget(a) + return a +} + +func (p *PlaylistList) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(p.container) +} + +func (p *PlaylistList) onRowTapped(id string) { + if p.OnNavTo != nil { + p.OnNavTo(id) + } +} + +type PlaylistListRow struct { + widget.BaseWidget + + ID string + OnTapped func() + + nameLabel *widget.Label + descrptionLabel *widget.Label + ownerLabel *widget.Label + trackCountLabel *widget.Label + + container *fyne.Container +} + +func NewPlaylistListRow(layout *layouts.ColumnsLayout) *PlaylistListRow { + a := &PlaylistListRow{ + nameLabel: widget.NewLabel(""), + descrptionLabel: widget.NewLabel(""), + ownerLabel: widget.NewLabel(""), + trackCountLabel: widget.NewLabel(""), + } + a.ownerLabel.Wrapping = fyne.TextTruncate + a.container = container.New(layout, a.nameLabel, a.descrptionLabel, a.ownerLabel, a.trackCountLabel) + a.ExtendBaseWidget(a) + return a +} + +func (a *PlaylistListRow) Tapped(*fyne.PointEvent) { + if a.OnTapped != nil { + a.OnTapped() + } +} + +func (a *PlaylistListRow) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(a.container) +} diff --git a/ui/browsing/router.go b/ui/browsing/router.go index dc7b8e0..31aef17 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -51,6 +51,10 @@ func GenresRoute() Route { return Route{Page: Genres} } +func PlaylistsRoute() Route { + return Route{Page: Playlists} +} + func ArtistsRoute() Route { return Route{Page: Artists} } @@ -105,6 +109,8 @@ func (r Router) CreatePage(rte Route) Page { return NewGenrePage(rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) case Genres: return NewArtistsGenresPage(true, r.App.ServerManager, r.OpenRoute) + case Playlists: + return NewPlaylistsPage(r.App.ServerManager, r.OpenRoute) } return nil } diff --git a/ui/mainwindow.go b/ui/mainwindow.go index e47d20a..adcc4c9 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -87,6 +87,9 @@ func (m *MainWindow) addNavigationButtons() { m.BrowsingPane.AddNavigationButton(res.ResTheatermasksInvertPng, func() { m.Router.OpenRoute(browsing.GenresRoute()) }) + m.BrowsingPane.AddNavigationButton(res.ResPlaylistInvertPng, func() { + m.Router.OpenRoute(browsing.PlaylistsRoute()) + }) } func (m *MainWindow) addShortcuts() {