From d01ac697bd3ea0fa5cf5ca4c6640084268ce8af6 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Thu, 23 Feb 2023 17:23:14 -0800 Subject: [PATCH] make artist and album hyperlinks in tracklist; refactor navigation to the controller package --- ui/bottompanel.go | 8 +-- ui/browsing/albumpage.go | 13 ++-- ui/browsing/albumspage.go | 21 +++--- ui/browsing/artistpage.go | 20 +++--- ui/browsing/artistsgenrespage.go | 23 +++---- ui/browsing/browsingpane.go | 5 +- ui/browsing/favoritespage.go | 17 ++--- ui/browsing/genrepage.go | 21 +++--- ui/browsing/nowplayingpage.go | 10 ++- ui/browsing/playlistpage.go | 8 +-- ui/browsing/playlistspage.go | 23 +++---- ui/browsing/router.go | 108 +++++++------------------------ ui/controller/controller.go | 13 ++++ ui/controller/routes.go | 63 ++++++++++++++++++ ui/mainwindow.go | 23 +++---- ui/widgets/tracklist.go | 37 ++++++++--- 16 files changed, 218 insertions(+), 195 deletions(-) create mode 100644 ui/controller/routes.go diff --git a/ui/bottompanel.go b/ui/bottompanel.go index b21561a..f1bde78 100644 --- a/ui/bottompanel.go +++ b/ui/bottompanel.go @@ -5,7 +5,7 @@ import ( "image" "supersonic/backend" "supersonic/player" - "supersonic/ui/browsing" + "supersonic/ui/controller" "supersonic/ui/layouts" "supersonic/ui/widgets" "time" @@ -30,7 +30,7 @@ type BottomPanel struct { var _ fyne.Widget = (*BottomPanel)(nil) -func NewBottomPanel(p *player.Player, nav func(browsing.Route)) *BottomPanel { +func NewBottomPanel(p *player.Player, nav func(controller.Route)) *BottomPanel { bp := &BottomPanel{} bp.ExtendBaseWidget(bp) p.OnPaused(func() { @@ -45,10 +45,10 @@ func NewBottomPanel(p *player.Player, nav func(browsing.Route)) *BottomPanel { bp.NowPlaying = widgets.NewNowPlayingCard() bp.NowPlaying.OnAlbumNameTapped(func() { - nav(browsing.AlbumRoute(bp.playbackManager.NowPlaying().AlbumID)) + nav(controller.AlbumRoute(bp.playbackManager.NowPlaying().AlbumID)) }) bp.NowPlaying.OnArtistNameTapped(func() { - nav(browsing.ArtistRoute(bp.playbackManager.NowPlaying().ArtistID)) + nav(controller.ArtistRoute(bp.playbackManager.NowPlaying().ArtistID)) }) bp.Controls = widgets.NewPlayerControls() bp.Controls.OnPlayPause(func() { diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 76d44ab..559762c 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -38,7 +38,6 @@ type albumPageState struct { im *backend.ImageManager sm *backend.ServerManager contr controller.Controller - nav func(Route) } func NewAlbumPage( @@ -49,7 +48,6 @@ func NewAlbumPage( lm *backend.LibraryManager, im *backend.ImageManager, contr controller.Controller, - nav func(Route), ) *AlbumPage { a := &AlbumPage{ albumPageState: albumPageState{ @@ -59,7 +57,6 @@ func NewAlbumPage( pm: pm, lm: lm, im: im, - nav: nav, contr: contr, }, } @@ -90,8 +87,8 @@ func (a *AlbumPage) Save() SavedPage { return &s } -func (a *AlbumPage) Route() Route { - return AlbumRoute(a.albumID) +func (a *AlbumPage) Route() controller.Route { + return controller.AlbumRoute(a.albumID) } func (a *AlbumPage) OnSongChange(song *subsonic.Child, lastScrobbledIfAny *subsonic.Child) { @@ -166,11 +163,11 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader { } a.artistLabel = widgets.NewCustomHyperlink() a.artistLabel.OnTapped = func() { - page.nav(ArtistRoute(a.artistID)) + page.contr.NavigateTo(controller.ArtistRoute(a.artistID)) } a.genreLabel = widgets.NewCustomHyperlink() a.genreLabel.OnTapped = func() { - page.nav(GenreRoute(a.genre)) + page.contr.NavigateTo(controller.GenreRoute(a.genre)) } a.miscLabel = widget.NewLabel("") playButton := widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() { @@ -249,5 +246,5 @@ func formatMiscLabelStr(a *subsonic.AlbumID3) string { } func (s *albumPageState) Restore() Page { - return NewAlbumPage(s.albumID, s.cfg, s.sm, s.pm, s.lm, s.im, s.contr, s.nav) + return NewAlbumPage(s.albumID, s.cfg, s.sm, s.pm, s.lm, s.im, s.contr) } diff --git a/ui/browsing/albumspage.go b/ui/browsing/albumspage.go index 5d0b2a1..ac00a30 100644 --- a/ui/browsing/albumspage.go +++ b/ui/browsing/albumspage.go @@ -2,6 +2,7 @@ package browsing import ( "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/widgets" "fyne.io/fyne/v2" @@ -17,10 +18,10 @@ type AlbumsPage struct { widget.BaseWidget title string + contr controller.Controller pm *backend.PlaybackManager im *backend.ImageManager lm *backend.LibraryManager - nav func(Route) grid *widgets.AlbumGrid searchGrid *widgets.AlbumGrid searcher *widgets.Searcher @@ -51,13 +52,13 @@ func (s *selectWidget) MinSize() fyne.Size { return fyne.NewSize(170, s.height) } -func NewAlbumsPage(title string, sortOrder string, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumsPage { +func NewAlbumsPage(title string, sortOrder string, contr controller.Controller, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager) *AlbumsPage { a := &AlbumsPage{ title: title, + contr: contr, pm: pm, lm: lm, im: im, - nav: nav, } a.ExtendBaseWidget(a) @@ -99,10 +100,10 @@ func (a *AlbumsPage) createContainer(searchgrid bool) { func restoreAlbumsPage(saved *savedAlbumsPage) *AlbumsPage { a := &AlbumsPage{ title: saved.title, + contr: saved.contr, pm: saved.pm, lm: saved.lm, im: saved.im, - nav: saved.nav, } a.ExtendBaseWidget(a) @@ -138,8 +139,8 @@ func (a *AlbumsPage) OnSearched(query string) { a.doSearch(query) } -func (a *AlbumsPage) Route() Route { - return AlbumsRoute(backend.AlbumSortOrder(a.sortOrder.Selected)) +func (a *AlbumsPage) Route() controller.Route { + return controller.AlbumsRoute(backend.AlbumSortOrder(a.sortOrder.Selected)) } var _ Searchable = (*AlbumsPage)(nil) @@ -160,10 +161,10 @@ func (a *AlbumsPage) Reload() { func (a *AlbumsPage) Save() SavedPage { sa := &savedAlbumsPage{ title: a.title, + contr: a.contr, pm: a.pm, lm: a.lm, im: a.im, - nav: a.nav, searchText: a.searchText, sortOrder: a.sortOrder.Selected, gridState: a.grid.SaveToState(), @@ -192,11 +193,11 @@ func (a *AlbumsPage) onPlayAlbum(albumID string) { } func (a *AlbumsPage) onShowArtistPage(artistID string) { - a.nav(ArtistRoute(artistID)) + a.contr.NavigateTo(controller.ArtistRoute(artistID)) } func (a *AlbumsPage) onShowAlbumPage(albumID string) { - a.nav(AlbumRoute(albumID)) + a.contr.NavigateTo(controller.AlbumRoute(albumID)) } func (a *AlbumsPage) onSortOrderChanged(order string) { @@ -215,10 +216,10 @@ func (a *AlbumsPage) CreateRenderer() fyne.WidgetRenderer { type savedAlbumsPage struct { title string searchText string + contr controller.Controller pm *backend.PlaybackManager lm *backend.LibraryManager im *backend.ImageManager - nav func(Route) sortOrder string gridState widgets.AlbumGridState searchGridState widgets.AlbumGridState diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index b1c291c..15ccc9a 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -26,7 +26,6 @@ type artistPageState struct { artistID string sm *backend.ServerManager im *backend.ImageManager - nav func(Route) contr controller.Controller } @@ -41,16 +40,15 @@ type ArtistPage struct { OnPlayAlbum func(string, int) } -func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, contr controller.Controller, nav func(Route)) *ArtistPage { +func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, contr controller.Controller) *ArtistPage { a := &ArtistPage{artistPageState: artistPageState{ artistID: artistID, sm: sm, im: im, - nav: nav, contr: contr, }} a.ExtendBaseWidget(a) - a.header = NewArtistPageHeader(a, nav) + a.header = NewArtistPageHeader(a) a.container = container.NewBorder( container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header), nil, nil, nil, layout.NewSpacer()) @@ -58,8 +56,8 @@ func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.Image return a } -func (a *ArtistPage) Route() Route { - return ArtistRoute(a.artistID) +func (a *ArtistPage) Route() controller.Route { + return controller.ArtistRoute(a.artistID) } func (a *ArtistPage) SetPlayAlbumCallback(cb func(string, int)) { @@ -82,7 +80,7 @@ func (a *ArtistPage) onPlayAlbum(albumID string) { } func (a *ArtistPage) onShowAlbumPage(albumID string) { - a.nav(AlbumRoute(albumID)) + a.contr.NavigateTo(controller.AlbumRoute(albumID)) } // should be called asynchronously @@ -111,13 +109,12 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer { } func (s *artistPageState) Restore() Page { - return NewArtistPage(s.artistID, s.sm, s.im, s.contr, s.nav) + return NewArtistPage(s.artistID, s.sm, s.im, s.contr) } type ArtistPageHeader struct { widget.BaseWidget - nav func(Route) artistID string artistPage *ArtistPage artistImage *widgets.ImagePlaceholder @@ -128,9 +125,8 @@ type ArtistPageHeader struct { container *fyne.Container } -func NewArtistPageHeader(page *ArtistPage, nav func(Route)) *ArtistPageHeader { +func NewArtistPageHeader(page *ArtistPage) *ArtistPageHeader { a := &ArtistPageHeader{ - nav: nav, artistPage: page, titleDisp: widget.NewRichTextWithText(""), biographyDisp: widget.NewRichTextWithText("Artist biography not available."), @@ -181,7 +177,7 @@ func (a *ArtistPageHeader) UpdateInfo(info *subsonic.ArtistInfo2) { h.NoTruncate = true h.SetText(art.Name) h.OnTapped = func(id string) func() { - return func() { a.nav(ArtistRoute(id)) } + return func() { a.artistPage.contr.NavigateTo(controller.ArtistRoute(id)) } }(art.ID) a.similarArtists.Add(h) } diff --git a/ui/browsing/artistsgenrespage.go b/ui/browsing/artistsgenrespage.go index b10cdb1..d4cc069 100644 --- a/ui/browsing/artistsgenrespage.go +++ b/ui/browsing/artistsgenrespage.go @@ -3,6 +3,7 @@ package browsing import ( "log" "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/layouts" "supersonic/ui/widgets" "time" @@ -20,22 +21,22 @@ type ArtistsGenresPage struct { widget.BaseWidget isGenresPage bool + contr controller.Controller sm *backend.ServerManager - nav func(Route) titleDisp *widget.RichText container *fyne.Container list *widgets.ArtistGenrePlaylist } -func NewArtistsGenresPage(isGenresPage bool, sm *backend.ServerManager, nav func(Route)) *ArtistsGenresPage { +func NewArtistsGenresPage(isGenresPage bool, contr controller.Controller, sm *backend.ServerManager) *ArtistsGenresPage { title := "Artists" if isGenresPage { title = "Genres" } a := &ArtistsGenresPage{ isGenresPage: isGenresPage, + contr: contr, sm: sm, - nav: nav, titleDisp: widget.NewRichTextWithText(title), } a.ExtendBaseWidget(a) @@ -45,9 +46,9 @@ func NewArtistsGenresPage(isGenresPage bool, sm *backend.ServerManager, nav func a.list.ShowTrackCount = isGenresPage a.list.OnNavTo = func(id string) { if a.isGenresPage { - nav(GenreRoute(id)) + a.contr.NavigateTo(controller.GenreRoute(id)) } else { - nav(ArtistRoute(id)) + a.contr.NavigateTo(controller.ArtistRoute(id)) } } a.buildContainer() @@ -73,11 +74,11 @@ func (a *ArtistsGenresPage) load() { a.Refresh() } -func (a *ArtistsGenresPage) Route() Route { +func (a *ArtistsGenresPage) Route() controller.Route { if a.isGenresPage { - return GenresRoute() + return controller.GenresRoute() } - return ArtistsRoute() + return controller.ArtistsRoute() } func (a *ArtistsGenresPage) Reload() { @@ -87,19 +88,19 @@ func (a *ArtistsGenresPage) Reload() { func (a *ArtistsGenresPage) Save() SavedPage { return &savedArtistsGenresPage{ isGenresPage: a.isGenresPage, + contr: a.contr, sm: a.sm, - nav: a.nav, } } type savedArtistsGenresPage struct { isGenresPage bool + contr controller.Controller sm *backend.ServerManager - nav func(Route) } func (s *savedArtistsGenresPage) Restore() Page { - return NewArtistsGenresPage(s.isGenresPage, s.sm, s.nav) + return NewArtistsGenresPage(s.isGenresPage, s.contr, s.sm) } func (a *ArtistsGenresPage) buildArtistListModel(artists *subsonic.ArtistsID3) []widgets.ArtistGenrePlaylistItemModel { diff --git a/ui/browsing/browsingpane.go b/ui/browsing/browsingpane.go index 194402a..091d5bd 100644 --- a/ui/browsing/browsingpane.go +++ b/ui/browsing/browsingpane.go @@ -3,6 +3,7 @@ package browsing import ( "image/color" "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/layouts" "fyne.io/fyne/v2" @@ -19,7 +20,7 @@ type Page interface { Save() SavedPage Reload() - Route() Route + Route() controller.Route } type SavedPage interface { @@ -216,7 +217,7 @@ var _ Page = (*blankPage)(nil) func (p *blankPage) Reload() {} -func (p *blankPage) Route() Route { return Route{Page: Blank} } +func (p *blankPage) Route() controller.Route { return controller.Route{Page: controller.Blank} } func (p *blankPage) Save() SavedPage { return p } diff --git a/ui/browsing/favoritespage.go b/ui/browsing/favoritespage.go index b40eb74..e8bfb39 100644 --- a/ui/browsing/favoritespage.go +++ b/ui/browsing/favoritespage.go @@ -26,7 +26,6 @@ type FavoritesPage struct { im *backend.ImageManager sm *backend.ServerManager lm *backend.LibraryManager - nav func(Route) searchText string nowPlayingID string @@ -42,7 +41,7 @@ type FavoritesPage struct { container *fyne.Container } -func NewFavoritesPage(cfg *backend.FavoritesPageConfig, contr controller.Controller, sm *backend.ServerManager, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *FavoritesPage { +func NewFavoritesPage(cfg *backend.FavoritesPageConfig, contr controller.Controller, sm *backend.ServerManager, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager) *FavoritesPage { a := &FavoritesPage{ cfg: cfg, contr: contr, @@ -50,7 +49,6 @@ func NewFavoritesPage(cfg *backend.FavoritesPageConfig, contr controller.Control lm: lm, sm: sm, im: im, - nav: nav, } a.ExtendBaseWidget(a) a.createHeader(0, "") @@ -106,7 +104,6 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage { lm: saved.lm, sm: saved.sm, im: saved.im, - nav: saved.nav, } a.ExtendBaseWidget(a) a.createHeader(saved.activeToggleBtn, saved.searchText) @@ -126,8 +123,8 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage { return a } -func (a *FavoritesPage) Route() Route { - return FavoritesRoute() +func (a *FavoritesPage) Route() controller.Route { + return controller.FavoritesRoute() } func (a *FavoritesPage) Reload() { @@ -179,7 +176,6 @@ func (a *FavoritesPage) Save() SavedPage { sm: a.sm, im: a.im, lm: a.lm, - nav: a.nav, searchText: a.searchText, gridState: a.grid.SaveToState(), activeToggleBtn: a.toggleBtns.ActivatedButtonIndex(), @@ -266,7 +262,7 @@ func (a *FavoritesPage) onShowFavoriteArtists() { artistList := widgets.NewArtistGenrePlaylist(model) artistList.ShowAlbumCount = true artistList.OnNavTo = func(artistID string) { - a.nav(ArtistRoute(artistID)) + a.contr.NavigateTo(controller.ArtistRoute(artistID)) } a.artistListCtr = container.New( &layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 5, PadBottom: 15}, @@ -330,11 +326,11 @@ func (a *FavoritesPage) onPlayAlbum(albumID string) { } func (a *FavoritesPage) onShowAlbumPage(albumID string) { - a.nav(AlbumRoute(albumID)) + a.contr.NavigateTo(controller.AlbumRoute(albumID)) } func (a *FavoritesPage) onShowArtistPage(artistID string) { - a.nav(ArtistRoute(artistID)) + a.contr.NavigateTo(controller.ArtistRoute(artistID)) } func (a *FavoritesPage) CreateRenderer() fyne.WidgetRenderer { @@ -353,7 +349,6 @@ type savedFavoritesPage struct { searchGridState widgets.AlbumGridState searchText string activeToggleBtn int - nav func(Route) } func (s *savedFavoritesPage) Restore() Page { diff --git a/ui/browsing/genrepage.go b/ui/browsing/genrepage.go index b75b5ba..6ab3047 100644 --- a/ui/browsing/genrepage.go +++ b/ui/browsing/genrepage.go @@ -2,6 +2,7 @@ package browsing import ( "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/widgets" "fyne.io/fyne/v2" @@ -17,10 +18,10 @@ type GenrePage struct { widget.BaseWidget genre string + contr controller.Controller im *backend.ImageManager pm *backend.PlaybackManager lm *backend.LibraryManager - nav func(Route) grid *widgets.AlbumGrid searchGrid *widgets.AlbumGrid searcher *widgets.Searcher @@ -32,13 +33,13 @@ type GenrePage struct { container *fyne.Container } -func NewGenrePage(genre string, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *GenrePage { +func NewGenrePage(genre string, contr controller.Controller, pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager) *GenrePage { g := &GenrePage{ genre: genre, + contr: contr, pm: pm, lm: lm, im: im, - nav: nav, } g.ExtendBaseWidget(g) @@ -76,10 +77,10 @@ func (g *GenrePage) createContainer(searchGrid bool) { func restoreGenrePage(saved *savedGenrePage) *GenrePage { g := &GenrePage{ genre: saved.genre, + contr: saved.contr, pm: saved.pm, lm: saved.lm, im: saved.im, - nav: saved.nav, } g.ExtendBaseWidget(g) @@ -103,8 +104,8 @@ func (g *GenrePage) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(g.container) } -func (a *GenrePage) Route() Route { - return GenreRoute(a.genre) +func (a *GenrePage) Route() controller.Route { + return controller.GenreRoute(a.genre) } func (a *GenrePage) SetPlayAlbumCallback(cb func(string, int)) { @@ -124,10 +125,10 @@ func (g *GenrePage) Save() SavedPage { sg := &savedGenrePage{ genre: g.genre, searchText: g.searchText, + contr: g.contr, pm: g.pm, lm: g.lm, im: g.im, - nav: g.nav, gridState: g.grid.SaveToState(), } if g.searchGrid != nil { @@ -147,11 +148,11 @@ func (a *GenrePage) onPlayAlbum(albumID string) { } func (a *GenrePage) onShowArtistPage(artistID string) { - a.nav(ArtistRoute(artistID)) + a.contr.NavigateTo(controller.ArtistRoute(artistID)) } func (a *GenrePage) onShowAlbumPage(albumID string) { - a.nav(AlbumRoute(albumID)) + a.contr.NavigateTo(controller.AlbumRoute(albumID)) } func (g *GenrePage) OnSearched(query string) { @@ -186,10 +187,10 @@ func (g *GenrePage) doSearch(query string) { type savedGenrePage struct { genre string searchText string + contr controller.Controller pm *backend.PlaybackManager lm *backend.LibraryManager im *backend.ImageManager - nav func(Route) gridState widgets.AlbumGridState searchGridState widgets.AlbumGridState } diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index a66131c..be85708 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -29,7 +29,6 @@ type nowPlayingPageState struct { conf *backend.NowPlayingPageConfig sm *backend.ServerManager pm *backend.PlaybackManager - nav func(Route) } func NewNowPlayingPage( @@ -37,9 +36,8 @@ func NewNowPlayingPage( conf *backend.NowPlayingPageConfig, sm *backend.ServerManager, pm *backend.PlaybackManager, - nav func(Route), ) *NowPlayingPage { - a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, conf: conf, sm: sm, pm: pm, nav: nav}} + a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, conf: conf, sm: sm, pm: pm}} a.ExtendBaseWidget(a) a.tracklist = widgets.NewTracklist(nil) a.tracklist.SetVisibleColumns(conf.TracklistColumns) @@ -69,8 +67,8 @@ func (a *NowPlayingPage) Save() SavedPage { return &nps } -func (a *NowPlayingPage) Route() Route { - return NowPlayingRoute() +func (a *NowPlayingPage) Route() controller.Route { + return controller.NowPlayingRoute() } func (a *NowPlayingPage) Tapped(*fyne.PointEvent) { @@ -113,5 +111,5 @@ func (a *NowPlayingPage) load() { } func (s *nowPlayingPageState) Restore() Page { - return NewNowPlayingPage(s.contr, s.conf, s.sm, s.pm, s.nav) + return NewNowPlayingPage(s.contr, s.conf, s.sm, s.pm) } diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index bfd3cc3..db8f136 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -36,7 +36,6 @@ type playlistPageState struct { sm *backend.ServerManager pm *backend.PlaybackManager im *backend.ImageManager - nav func(Route) } func NewPlaylistPage( @@ -46,7 +45,6 @@ func NewPlaylistPage( sm *backend.ServerManager, pm *backend.PlaybackManager, im *backend.ImageManager, - nav func(Route), ) *PlaylistPage { a := &PlaylistPage{playlistPageState: playlistPageState{playlistID: playlistID, conf: conf, contr: contr, sm: sm, pm: pm, im: im}} a.ExtendBaseWidget(a) @@ -77,8 +75,8 @@ func (a *PlaylistPage) Save() SavedPage { return &p } -func (a *PlaylistPage) Route() Route { - return PlaylistRoute(a.playlistID) +func (a *PlaylistPage) Route() controller.Route { + return controller.PlaylistRoute(a.playlistID) } func (a *PlaylistPage) OnSongChange(song *subsonic.Child, lastScrobbledIfAny *subsonic.Child) { @@ -211,5 +209,5 @@ func (a *PlaylistPageHeader) formatPlaylistTrackTimeStr(p *subsonic.Playlist) st } func (s *playlistPageState) Restore() Page { - return NewPlaylistPage(s.playlistID, s.conf, s.contr, s.sm, s.pm, s.im, s.nav) + return NewPlaylistPage(s.playlistID, s.conf, s.contr, s.sm, s.pm, s.im) } diff --git a/ui/browsing/playlistspage.go b/ui/browsing/playlistspage.go index 4a2213e..715fe90 100644 --- a/ui/browsing/playlistspage.go +++ b/ui/browsing/playlistspage.go @@ -4,6 +4,7 @@ import ( "log" "strconv" "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/layouts" "supersonic/ui/widgets" @@ -17,24 +18,24 @@ import ( type PlaylistsPage struct { widget.BaseWidget + contr controller.Controller sm *backend.ServerManager - nav func(Route) titleDisp *widget.RichText container *fyne.Container list *PlaylistList } -func NewPlaylistsPage(sm *backend.ServerManager, nav func(Route)) *PlaylistsPage { +func NewPlaylistsPage(contr controller.Controller, sm *backend.ServerManager) *PlaylistsPage { a := &PlaylistsPage{ sm: sm, - nav: nav, + contr: contr, 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) { - nav(PlaylistRoute(id)) + a.contr.NavigateTo(controller.PlaylistRoute(id)) } a.buildContainer() go a.loadAsync() @@ -50,8 +51,8 @@ func (a *PlaylistsPage) loadAsync() { a.list.Refresh() } -func (a *PlaylistsPage) Route() Route { - return PlaylistsRoute() +func (a *PlaylistsPage) Route() controller.Route { + return controller.PlaylistsRoute() } func (a *PlaylistsPage) Reload() { @@ -60,18 +61,18 @@ func (a *PlaylistsPage) Reload() { func (a *PlaylistsPage) Save() SavedPage { return &savedPlaylistsPage{ - sm: a.sm, - nav: a.nav, + contr: a.contr, + sm: a.sm, } } type savedPlaylistsPage struct { - sm *backend.ServerManager - nav func(Route) + contr controller.Controller + sm *backend.ServerManager } func (s *savedPlaylistsPage) Restore() Page { - return NewPlaylistsPage(s.sm, s.nav) + return NewPlaylistsPage(s.contr, s.sm) } func (a *PlaylistsPage) buildContainer() { diff --git a/ui/browsing/router.go b/ui/browsing/router.go index beeeb79..63151b7 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -5,77 +5,17 @@ import ( "supersonic/ui/controller" ) -type PageName int - -const ( - Blank PageName = iota - Album - Albums - Artist - Artists - Genre - Genres - Favorites - NowPlaying - Playlist - Playlists -) - -type Route struct { - Page PageName - Arg string -} - -func AlbumsRoute(sortOrder backend.AlbumSortOrder) Route { - return Route{Page: Albums, Arg: string(sortOrder)} -} - -func ArtistRoute(artistID string) Route { - return Route{Page: Artist, Arg: artistID} -} - -func AlbumRoute(albumID string) Route { - return Route{Page: Album, Arg: albumID} -} - -func FavoritesRoute() Route { - return Route{Page: Favorites} -} - -func GenreRoute(genre string) Route { - return Route{Page: Genre, Arg: genre} -} - -func GenresRoute() Route { - return Route{Page: Genres} -} - -func PlaylistRoute(id string) Route { - return Route{Page: Playlist, Arg: id} -} -func PlaylistsRoute() Route { - return Route{Page: Playlists} -} - -func ArtistsRoute() Route { - return Route{Page: Artists} -} - -func NowPlayingRoute() Route { - return Route{Page: NowPlaying} -} - type NavigationHandler interface { SetPage(Page) } type Router struct { App *backend.App - Controller controller.Controller + Controller *controller.Controller Nav NavigationHandler } -func NewRouter(app *backend.App, controller controller.Controller, nav NavigationHandler) Router { +func NewRouter(app *backend.App, controller *controller.Controller, nav NavigationHandler) Router { r := Router{ App: app, Controller: controller, @@ -84,32 +24,32 @@ func NewRouter(app *backend.App, controller controller.Controller, nav Navigatio return r } -func (r Router) CreatePage(rte Route) Page { +func (r Router) CreatePage(rte controller.Route) Page { switch rte.Page { - case Album: - return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.Controller, r.OpenRoute) - case Albums: - return NewAlbumsPage("Albums", rte.Arg, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) - case Artist: - return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.Controller, r.OpenRoute) - case Artists: - return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute) - case Favorites: - return NewFavoritesPage(&r.App.Config.FavoritesPage, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) - case Genre: - return NewGenrePage(rte.Arg, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) - case Genres: - return NewArtistsGenresPage(true, r.App.ServerManager, r.OpenRoute) - case NowPlaying: - return NewNowPlayingPage(r.Controller, &r.App.Config.NowPlayingPage, r.App.ServerManager, r.App.PlaybackManager, r.OpenRoute) - case Playlist: - return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager, r.OpenRoute) - case Playlists: - return NewPlaylistsPage(r.App.ServerManager, r.OpenRoute) + case controller.Album: + return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, *r.Controller) + case controller.Albums: + return NewAlbumsPage("Albums", rte.Arg, *r.Controller, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager) + case controller.Artist: + return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, *r.Controller) + case controller.Artists: + return NewArtistsGenresPage(false, *r.Controller, r.App.ServerManager) + case controller.Favorites: + return NewFavoritesPage(&r.App.Config.FavoritesPage, *r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager) + case controller.Genre: + return NewGenrePage(rte.Arg, *r.Controller, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager) + case controller.Genres: + return NewArtistsGenresPage(true, *r.Controller, r.App.ServerManager) + case controller.NowPlaying: + return NewNowPlayingPage(*r.Controller, &r.App.Config.NowPlayingPage, r.App.ServerManager, r.App.PlaybackManager) + case controller.Playlist: + return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, *r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager) + case controller.Playlists: + return NewPlaylistsPage(*r.Controller, r.App.ServerManager) } return nil } -func (r Router) OpenRoute(rte Route) { +func (r Router) NavigateTo(rte controller.Route) { r.Nav.SetPage(r.CreatePage(rte)) } diff --git a/ui/controller/controller.go b/ui/controller/controller.go index be192bc..385f453 100644 --- a/ui/controller/controller.go +++ b/ui/controller/controller.go @@ -16,9 +16,16 @@ import ( "github.com/dweymouth/go-subsonic/subsonic" ) +type NavigationHandler func(Route) + type Controller struct { MainWindow fyne.Window App *backend.App + NavHandler NavigationHandler +} + +func (m Controller) NavigateTo(route Route) { + m.NavHandler(route) } func (m Controller) ShowPopUpImage(img image.Image) { @@ -66,6 +73,12 @@ func (m Controller) ConnectTracklistActions(tracklist *widgets.Tracklist) { m.App.PlaybackManager.OnTrackFavoriteStatusChanged(id, fav) } } + tracklist.OnShowAlbumPage = func(albumID string) { + m.NavigateTo(AlbumRoute(albumID)) + } + tracklist.OnShowArtistPage = func(artistID string) { + m.NavigateTo(ArtistRoute(artistID)) + } } func (m Controller) PromptForFirstServer() { diff --git a/ui/controller/routes.go b/ui/controller/routes.go new file mode 100644 index 0000000..78f73a9 --- /dev/null +++ b/ui/controller/routes.go @@ -0,0 +1,63 @@ +package controller + +import "supersonic/backend" + +type PageName int + +const ( + Blank PageName = iota + Album + Albums + Artist + Artists + Genre + Genres + Favorites + NowPlaying + Playlist + Playlists +) + +type Route struct { + Page PageName + Arg string +} + +func AlbumsRoute(sortOrder backend.AlbumSortOrder) Route { + return Route{Page: Albums, Arg: string(sortOrder)} +} + +func ArtistRoute(artistID string) Route { + return Route{Page: Artist, Arg: artistID} +} + +func AlbumRoute(albumID string) Route { + return Route{Page: Album, Arg: albumID} +} + +func FavoritesRoute() Route { + return Route{Page: Favorites} +} + +func GenreRoute(genre string) Route { + return Route{Page: Genre, Arg: genre} +} + +func GenresRoute() Route { + return Route{Page: Genres} +} + +func PlaylistRoute(id string) Route { + return Route{Page: Playlist, Arg: id} +} +func PlaylistsRoute() Route { + return Route{Page: Playlists} +} + +func ArtistsRoute() Route { + return Route{Page: Artists} +} + +func NowPlayingRoute() Route { + return Route{Page: NowPlaying} +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 52cb85a..3347e44 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -35,7 +35,7 @@ type MainWindow struct { App *backend.App Router browsing.Router - Controller controller.Controller + Controller *controller.Controller BrowsingPane *browsing.BrowsingPane BottomPanel *BottomPanel @@ -43,7 +43,7 @@ type MainWindow struct { } var ( - HomePage = browsing.AlbumsRoute(backend.AlbumSortRecentlyAdded) + HomePage = controller.AlbumsRoute(backend.AlbumSortRecentlyAdded) ) func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App, size fyne.Size) MainWindow { @@ -53,12 +53,13 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App, size fyne BrowsingPane: browsing.NewBrowsingPane(app), } - m.Controller = controller.Controller{ + m.Controller = &controller.Controller{ MainWindow: m.Window, App: app, } m.Router = browsing.NewRouter(app, m.Controller, m.BrowsingPane) - m.BottomPanel = NewBottomPanel(app.Player, m.Router.OpenRoute) + m.Controller.NavHandler = m.Router.NavigateTo + m.BottomPanel = NewBottomPanel(app.Player, m.Router.NavigateTo) m.BottomPanel.SetPlaybackManager(app.PlaybackManager) m.BottomPanel.ImageManager = app.ImageManager m.container = container.NewBorder(nil, m.BottomPanel, nil, nil, m.BrowsingPane) @@ -73,7 +74,7 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App, size fyne }) app.ServerManager.OnServerConnected(func() { m.BrowsingPane.EnableNavigationButtons() - m.Router.OpenRoute(HomePage) + m.Router.NavigateTo(HomePage) }) app.ServerManager.OnLogout(func() { m.BrowsingPane.DisableNavigationButtons() @@ -89,22 +90,22 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App, size fyne func (m *MainWindow) addNavigationButtons() { m.BrowsingPane.AddNavigationButton(res.ResHeadphonesInvertPng, func() { - m.Router.OpenRoute(browsing.NowPlayingRoute()) + m.Router.NavigateTo(controller.NowPlayingRoute()) }) m.BrowsingPane.AddNavigationButton(res.ResHeartFilledInvertPng, func() { - m.Router.OpenRoute(browsing.FavoritesRoute()) + m.Router.NavigateTo(controller.FavoritesRoute()) }) m.BrowsingPane.AddNavigationButton(res.ResDiscInvertPng, func() { - m.Router.OpenRoute(browsing.AlbumsRoute(backend.AlbumSortRecentlyAdded)) + m.Router.NavigateTo(controller.AlbumsRoute(backend.AlbumSortRecentlyAdded)) }) m.BrowsingPane.AddNavigationButton(res.ResPeopleInvertPng, func() { - m.Router.OpenRoute(browsing.ArtistsRoute()) + m.Router.NavigateTo(controller.ArtistsRoute()) }) m.BrowsingPane.AddNavigationButton(res.ResTheatermasksInvertPng, func() { - m.Router.OpenRoute(browsing.GenresRoute()) + m.Router.NavigateTo(controller.GenresRoute()) }) m.BrowsingPane.AddNavigationButton(res.ResPlaylistInvertPng, func() { - m.Router.OpenRoute(browsing.PlaylistsRoute()) + m.Router.NavigateTo(controller.PlaylistsRoute()) }) } diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 8041e50..876f623 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -46,6 +46,9 @@ type Tracklist struct { OnAddToPlaylist func(trackIDs []string) OnSetFavorite func(trackIDs []string, fav bool) + OnShowArtistPage func(artistID string) + OnShowAlbumPage func(albumID string) + visibleColumns []bool selectionMgr util.ListSelectionManager @@ -253,6 +256,18 @@ func (t *Tracklist) onSetFavorite(trackID string, fav bool) { } } +func (t *Tracklist) onArtistTapped(artistID string) { + if t.OnShowArtistPage != nil { + t.OnShowArtistPage(artistID) + } +} + +func (t *Tracklist) onAlbumTapped(albumID string) { + if t.OnShowAlbumPage != nil { + t.OnShowAlbumPage(albumID) + } +} + func (t *Tracklist) selectedTracks() []*subsonic.Child { sel := t.selectionMgr.GetSelection() tracks := make([]*subsonic.Child, 0, len(sel)) @@ -328,6 +343,8 @@ type TrackRow struct { trackIdx int trackNum int trackID string + artistID string + albumID string isPlaying bool isFavorite bool playCount int64 @@ -335,8 +352,8 @@ type TrackRow struct { num *widget.RichText name *widget.RichText - artist *widget.RichText - album *widget.RichText + artist *CustomHyperlink + album *CustomHyperlink dur *widget.RichText year *widget.RichText favorite *fyne.Container @@ -359,10 +376,10 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow t.num.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing t.name = widget.NewRichTextWithText("") t.name.Wrapping = fyne.TextTruncate - t.artist = widget.NewRichTextWithText("") - t.artist.Wrapping = fyne.TextTruncate - t.album = widget.NewRichTextWithText("") - t.album.Wrapping = fyne.TextTruncate + t.artist = NewCustomHyperlink() + t.artist.OnTapped = func() { tracklist.onArtistTapped(t.artistID) } + t.album = NewCustomHyperlink() + t.album.OnTapped = func() { tracklist.onAlbumTapped(t.albumID) } t.dur = widget.NewRichTextWithText("") t.dur.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing t.year = widget.NewRichTextWithText("") @@ -388,10 +405,12 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { // a new track (*subsonic.Child) if tr.ID != t.trackID { t.trackID = tr.ID + t.artistID = tr.ArtistID + t.albumID = tr.AlbumID t.name.Segments[0].(*widget.TextSegment).Text = tr.Title - t.artist.Segments[0].(*widget.TextSegment).Text = tr.Artist - t.album.Segments[0].(*widget.TextSegment).Text = tr.Album + t.artist.SetText(tr.Artist) + t.album.SetText(tr.Album) t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration)) t.year.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.Year) t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount)) @@ -418,8 +437,6 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { if isPlaying != t.isPlaying { t.isPlaying = isPlaying t.name.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.artist.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.album.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying t.dur.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying t.year.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying t.plays.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying