From cd6dce5e3fcf484f65b89e13956a1160260c5d40 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Thu, 9 Feb 2023 18:59:49 -0800 Subject: [PATCH] ui refactoring (create controller pkg) + beginning of add to playlist --- ui/browsing/albumpage.go | 35 ++++++++++---------- ui/browsing/artistpage.go | 27 ++++++++-------- ui/browsing/router.go | 27 ++++------------ ui/controller/controller.go | 64 +++++++++++++++++++++++++++++++++++++ ui/mainwindow.go | 8 ++++- ui/util/util.go | 22 ------------- 6 files changed, 109 insertions(+), 74 deletions(-) create mode 100644 ui/controller/controller.go diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 82bb207..9f23e86 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/layouts" "supersonic/ui/util" "supersonic/ui/widgets" @@ -30,13 +31,13 @@ type AlbumPage struct { } type albumPageState struct { - albumID string - lm *backend.LibraryManager - pm *backend.PlaybackManager - im *backend.ImageManager - sm *backend.ServerManager - popUpProvider util.PopUpProvider - nav func(Route) + albumID string + lm *backend.LibraryManager + pm *backend.PlaybackManager + im *backend.ImageManager + sm *backend.ServerManager + contr *controller.Controller + nav func(Route) } func NewAlbumPage( @@ -45,18 +46,18 @@ func NewAlbumPage( pm *backend.PlaybackManager, lm *backend.LibraryManager, im *backend.ImageManager, - popUpProvider util.PopUpProvider, + contr *controller.Controller, nav func(Route), ) *AlbumPage { a := &AlbumPage{ albumPageState: albumPageState{ - albumID: albumID, - sm: sm, - pm: pm, - lm: lm, - im: im, - nav: nav, - popUpProvider: popUpProvider, + albumID: albumID, + sm: sm, + pm: pm, + lm: lm, + im: im, + nav: nav, + contr: contr, }, } a.ExtendBaseWidget(a) @@ -241,7 +242,7 @@ func (a *AlbumPageHeader) showPopUpCover() { log.Printf("error getting full size album cover: %s", err.Error()) return } - util.ShowPopUpImage(cover, a.page.popUpProvider) + a.page.contr.ShowPopUpImage(cover) } func formatMiscLabelStr(a *subsonic.AlbumID3) string { @@ -249,5 +250,5 @@ func formatMiscLabelStr(a *subsonic.AlbumID3) string { } func (s *albumPageState) Restore() Page { - return NewAlbumPage(s.albumID, s.sm, s.pm, s.lm, s.im, s.popUpProvider, s.nav) + return NewAlbumPage(s.albumID, s.sm, s.pm, s.lm, s.im, s.contr, s.nav) } diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index b83d9a8..36ea5d7 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -7,6 +7,7 @@ import ( "strings" "supersonic/backend" "supersonic/res" + "supersonic/ui/controller" "supersonic/ui/layouts" "supersonic/ui/util" "supersonic/ui/widgets" @@ -22,11 +23,11 @@ import ( var _ fyne.Widget = (*ArtistPage)(nil) type artistPageState struct { - artistID string - sm *backend.ServerManager - im *backend.ImageManager - nav func(Route) - popUpProvider util.PopUpProvider + artistID string + sm *backend.ServerManager + im *backend.ImageManager + nav func(Route) + contr *controller.Controller } type ArtistPage struct { @@ -40,13 +41,13 @@ type ArtistPage struct { OnPlayAlbum func(string, int) } -func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, popUp util.PopUpProvider, nav func(Route)) *ArtistPage { +func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, contr *controller.Controller, nav func(Route)) *ArtistPage { a := &ArtistPage{artistPageState: artistPageState{ - artistID: artistID, - sm: sm, - im: im, - nav: nav, - popUpProvider: popUp, + artistID: artistID, + sm: sm, + im: im, + nav: nav, + contr: contr, }} a.ExtendBaseWidget(a) a.header = NewArtistPageHeader(a, nav) @@ -111,7 +112,7 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer { } func (s *artistPageState) Restore() Page { - return NewArtistPage(s.artistID, s.sm, s.im, s.popUpProvider, s.nav) + return NewArtistPage(s.artistID, s.sm, s.im, s.contr, s.nav) } type ArtistPageHeader struct { @@ -190,7 +191,7 @@ func (a *ArtistPageHeader) UpdateInfo(info *subsonic.ArtistInfo2) { return } a.artistImage.OnTapped = func() { - util.ShowPopUpImage(im, a.artistPage.popUpProvider) + a.artistPage.contr.ShowPopUpImage(im) } a.artistImage.SetImage(im, true /*tappable*/) } diff --git a/ui/browsing/router.go b/ui/browsing/router.go index 17d14d1..3eca1b7 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -2,10 +2,8 @@ package browsing import ( "supersonic/backend" + "supersonic/ui/controller" "supersonic/ui/util" - - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/widget" ) type PageName int @@ -69,42 +67,29 @@ type NavigationHandler interface { type Router struct { App *backend.App - MainWindow fyne.Window + Controller *controller.Controller Nav NavigationHandler pop util.PopUpProvider } -func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router { +func NewRouter(app *backend.App, controller *controller.Controller, nav NavigationHandler) Router { r := Router{ App: app, - MainWindow: mainWindow, + Controller: controller, Nav: nav, } - r.pop = &popUpProvider{window: r.MainWindow} return r } -type popUpProvider struct { - window fyne.Window -} - -func (p *popUpProvider) CreatePopUp(obj fyne.CanvasObject) *widget.PopUp { - return widget.NewPopUp(obj, p.window.Canvas()) -} - -func (p *popUpProvider) WindowSize() fyne.Size { - return p.window.Canvas().Size() -} - func (r Router) CreatePage(rte Route) Page { switch rte.Page { case Album: - return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.pop, r.OpenRoute) + return NewAlbumPage(rte.Arg, 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.LibraryManager, r.App.ImageManager, r.OpenRoute) case Artist: - return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.pop, r.OpenRoute) + 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: diff --git a/ui/controller/controller.go b/ui/controller/controller.go new file mode 100644 index 0000000..20326ca --- /dev/null +++ b/ui/controller/controller.go @@ -0,0 +1,64 @@ +package controller + +import ( + "image" + "log" + "supersonic/backend" + "supersonic/ui/dialogs" + "supersonic/ui/util" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" + "github.com/dweymouth/go-subsonic" +) + +type Controller struct { + MainWindow fyne.Window + App *backend.App +} + +func (m *Controller) ShowPopUpImage(img image.Image) { + im := canvas.NewImageFromImage(img) + im.FillMode = canvas.ImageFillContain + pop := widget.NewPopUp(im, m.MainWindow.Canvas()) + s := m.MainWindow.Canvas().Size() + var popS fyne.Size + if asp := util.ImageAspect(img); s.Width/s.Height > asp { + // window height is limiting factor + h := s.Height * 0.8 + popS = fyne.NewSize(h*asp, h) + } else { + w := s.Width * 0.8 + popS = fyne.NewSize(w, w*(1/asp)) + } + pop.Resize(popS) + pop.ShowAtPosition(fyne.NewPos( + (s.Width-popS.Width)/2, + (s.Height-popS.Height)/2, + )) +} + +// Show dialog to prompt for playlist. +// Depending on the results of that dialog, potentially create a new playlist +// Add tracks to the user-specified playlist +func (m *Controller) DoAddTracksToPlaylistWorkflow(tracks []*subsonic.Child) { + pls, err := m.App.LibraryManager.GetUserOwnedPlaylists() + if err != nil { + // TODO: surface this error to user + log.Printf("error getting user-owned playlists: %s", err.Error()) + return + } + plNames := make([]string, 0, len(pls)) + for _, pl := range pls { + plNames = append(plNames, pl.Name) + } + + dlg := dialogs.NewAddToPlaylistDialog("Add to Playlist", plNames) + pop := widget.NewModalPopUp(dlg, m.MainWindow.Canvas()) + dlg.OnCanceled = pop.Hide + dlg.OnSubmit = func(playlistChoice int, newPlaylistName string) { + pop.Hide() + // call server to add tracks to playlist + } +} diff --git a/ui/mainwindow.go b/ui/mainwindow.go index 0111a6b..a24b283 100644 --- a/ui/mainwindow.go +++ b/ui/mainwindow.go @@ -4,6 +4,7 @@ import ( "supersonic/backend" "supersonic/res" "supersonic/ui/browsing" + "supersonic/ui/controller" "supersonic/ui/os" "supersonic/ui/widgets" @@ -26,6 +27,7 @@ type MainWindow struct { App *backend.App Router browsing.Router + Controller *controller.Controller BrowsingPane *browsing.BrowsingPane BottomPanel *BottomPanel @@ -43,7 +45,11 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App, size fyne BrowsingPane: browsing.NewBrowsingPane(app), } - m.Router = browsing.NewRouter(app, m.Window, m.BrowsingPane) + 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.BottomPanel.SetPlaybackManager(app.PlaybackManager) m.BottomPanel.ImageManager = app.ImageManager diff --git a/ui/util/util.go b/ui/util/util.go index dee6e3c..edce626 100644 --- a/ui/util/util.go +++ b/ui/util/util.go @@ -7,7 +7,6 @@ import ( "strings" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/widget" "golang.org/x/net/html" ) @@ -33,27 +32,6 @@ type PopUpProvider interface { WindowSize() fyne.Size } -func ShowPopUpImage(img image.Image, popUpProvider PopUpProvider) { - im := canvas.NewImageFromImage(img) - im.FillMode = canvas.ImageFillContain - pop := popUpProvider.CreatePopUp(im) - s := popUpProvider.WindowSize() - var popS fyne.Size - if asp := ImageAspect(img); s.Width/s.Height > asp { - // window height is limiting factor - h := s.Height * 0.8 - popS = fyne.NewSize(h*asp, h) - } else { - w := s.Width * 0.8 - popS = fyne.NewSize(w, w*(1/asp)) - } - pop.Resize(popS) - pop.ShowAtPosition(fyne.NewPos( - (s.Width-popS.Width)/2, - (s.Height-popS.Height)/2, - )) -} - func RichTextSegsFromHTMLString(s string) []widget.RichTextSegment { tokr := html.NewTokenizer(strings.NewReader(s)) var segs []widget.RichTextSegment