Merge pull request #16 from dweymouth/develop

Add artist page and navigation
This commit is contained in:
Drew Weymouth
2023-01-01 10:32:50 -08:00
committed by GitHub
9 changed files with 372 additions and 49 deletions
+4
View File
@@ -37,6 +37,10 @@ func StartupApp() (*App, error) {
a := &App{} a := &App{}
a.bgrndCtx, a.cancel = context.WithCancel(context.Background()) a.bgrndCtx, a.cancel = context.WithCancel(context.Background())
log.Printf("Starting %s...", AppName)
log.Printf("Using config dir: %s", configdir.LocalConfig(AppName))
log.Printf("Using cache dir: %s", configdir.LocalCache(AppName))
a.readConfig() a.readConfig()
if err := a.initMPV(); err != nil { if err := a.initMPV(); err != nil {
+48 -22
View File
@@ -3,6 +3,7 @@ package ui
import ( import (
"context" "context"
"image" "image"
"log"
"supersonic/backend" "supersonic/backend"
"supersonic/ui/widgets" "supersonic/ui/widgets"
@@ -21,41 +22,35 @@ type AlbumGrid struct {
iter backend.AlbumIterator iter backend.AlbumIterator
fetching bool fetching bool
done bool done bool
showYear bool
imageFetcher ImageFetcher imageFetcher ImageFetcher
OnPlayAlbum func(string) OnPlayAlbum func(string)
OnShowArtistPage func(string)
} }
var _ fyne.Widget = (*AlbumGrid)(nil) var _ fyne.Widget = (*AlbumGrid)(nil)
func NewAlbumGrid(iter backend.AlbumIterator, fetch ImageFetcher) *AlbumGrid { func NewFixedAlbumGrid(albums []*subsonic.AlbumID3, fetch ImageFetcher, showYear bool) *AlbumGrid {
ag := &AlbumGrid{
albums: albums,
done: true,
imageFetcher: fetch,
showYear: showYear,
}
ag.ExtendBaseWidget(ag)
ag.createGridWrapList()
return ag
}
func NewAlbumGrid(iter backend.AlbumIterator, fetch ImageFetcher, showYear bool) *AlbumGrid {
ag := &AlbumGrid{ ag := &AlbumGrid{
iter: iter, iter: iter,
imageFetcher: fetch, imageFetcher: fetch,
} }
ag.ExtendBaseWidget(ag) ag.ExtendBaseWidget(ag)
g := widget.NewGridWrapList( ag.createGridWrapList()
func() int {
return len(ag.albums)
},
// create func
func() fyne.CanvasObject {
ac := widgets.NewAlbumCard()
ac.OnPlay = func() {
if ag.OnPlayAlbum != nil {
ag.OnPlayAlbum(ac.AlbumID())
}
}
return ac
},
// update func
func(itemID int, obj fyne.CanvasObject) {
ac := obj.(*widgets.AlbumCard)
ag.doUpdateAlbumCard(itemID, ac)
},
)
ag.grid = g
// fetch initial albums // fetch initial albums
ag.fetchMoreAlbums(36) ag.fetchMoreAlbums(36)
@@ -75,6 +70,35 @@ func (ag *AlbumGrid) Reset(iter backend.AlbumIterator) {
ag.fetchMoreAlbums(36) ag.fetchMoreAlbums(36)
} }
func (ag *AlbumGrid) createGridWrapList() {
g := widget.NewGridWrapList(
func() int {
return len(ag.albums)
},
// create func
func() fyne.CanvasObject {
ac := widgets.NewAlbumCard(ag.showYear)
ac.OnPlay = func() {
if ag.OnPlayAlbum != nil {
ag.OnPlayAlbum(ac.AlbumID())
}
}
ac.OnShowArtistPage = func() {
if ag.OnShowArtistPage != nil {
ag.OnShowArtistPage(ac.ArtistID())
}
}
return ac
},
// update func
func(itemID int, obj fyne.CanvasObject) {
ac := obj.(*widgets.AlbumCard)
ag.doUpdateAlbumCard(itemID, ac)
},
)
ag.grid = g
}
func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *widgets.AlbumCard) { func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *widgets.AlbumCard) {
album := ag.albums[albumIdx] album := ag.albums[albumIdx]
if ac.PrevAlbumID == album.ID { if ac.PrevAlbumID == album.ID {
@@ -99,6 +123,8 @@ func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *widgets.AlbumCard) {
if err == nil { if err == nil {
ac.Cover.SetImage(i) ac.Cover.SetImage(i)
ac.Cover.Refresh() ac.Cover.Refresh()
} else {
log.Printf("error fetching image: %s", err.Error())
} }
} }
}(ctx) }(ctx)
+17 -4
View File
@@ -18,6 +18,7 @@ type AlbumsPage struct {
im *backend.ImageManager im *backend.ImageManager
lm *backend.LibraryManager lm *backend.LibraryManager
nav func(Route)
grid *AlbumGrid grid *AlbumGrid
searchGrid *AlbumGrid searchGrid *AlbumGrid
searchText string searchText string
@@ -48,10 +49,11 @@ func (s *selectWidget) MinSize() fyne.Size {
return fyne.NewSize(170, s.height) return fyne.NewSize(170, s.height)
} }
func NewAlbumsPage(title string, lm *backend.LibraryManager, im *backend.ImageManager) *AlbumsPage { func NewAlbumsPage(title string, sortOrder string, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumsPage {
a := &AlbumsPage{ a := &AlbumsPage{
lm: lm, lm: lm,
im: im, im: im,
nav: nav,
} }
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
@@ -60,11 +62,13 @@ func NewAlbumsPage(title string, lm *backend.LibraryManager, im *backend.ImageMa
SizeName: theme.SizeNameHeadingText, SizeName: theme.SizeNameHeadingText,
} }
a.sortOrder = NewSelect(backend.AlbumSortOrders, nil) a.sortOrder = NewSelect(backend.AlbumSortOrders, nil)
a.sortOrder.Selected = a.sortOrder.Options[0] a.sortOrder.Selected = sortOrder
a.sortOrder.OnChanged = a.onSortOrderChanged a.sortOrder.OnChanged = a.onSortOrderChanged
sortVbox := container.NewVBox(layout.NewSpacer(), a.sortOrder, layout.NewSpacer()) sortVbox := container.NewVBox(layout.NewSpacer(), a.sortOrder, layout.NewSpacer())
a.grid = NewAlbumGrid(lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected)), im.GetAlbumThumbnail) iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
a.grid = NewAlbumGrid(iter, im.GetAlbumThumbnail, false /*showYear*/)
a.grid.OnPlayAlbum = a.onPlayAlbum a.grid.OnPlayAlbum = a.onPlayAlbum
a.grid.OnShowArtistPage = a.onShowArtistPage
a.container = container.NewBorder( a.container = container.NewBorder(
container.NewHBox(widgets.NewHSpace(15), a.titleDisp, sortVbox), container.NewHBox(widgets.NewHSpace(15), a.titleDisp, sortVbox),
nil, nil,
@@ -86,8 +90,9 @@ func (a *AlbumsPage) OnSearched(query string) {
return return
} }
if a.searchGrid == nil { if a.searchGrid == nil {
a.searchGrid = NewAlbumGrid(a.lm.SearchIter(query), a.im.GetAlbumThumbnail) a.searchGrid = NewAlbumGrid(a.lm.SearchIter(query), a.im.GetAlbumThumbnail, false /*showYear*/)
a.searchGrid.OnPlayAlbum = a.onPlayAlbum a.searchGrid.OnPlayAlbum = a.onPlayAlbum
a.searchGrid.OnShowArtistPage = a.onShowArtistPage
} else { } else {
a.searchGrid.Reset(a.lm.SearchIter(query)) a.searchGrid.Reset(a.lm.SearchIter(query))
} }
@@ -95,12 +100,20 @@ func (a *AlbumsPage) OnSearched(query string) {
a.Refresh() a.Refresh()
} }
func (a *AlbumsPage) SetPlayAlbumCallback(cb func(string)) {
a.OnPlayAlbum = cb
}
func (a *AlbumsPage) onPlayAlbum(albumID string) { func (a *AlbumsPage) onPlayAlbum(albumID string) {
if a.OnPlayAlbum != nil { if a.OnPlayAlbum != nil {
a.OnPlayAlbum(albumID) a.OnPlayAlbum(albumID)
} }
} }
func (a *AlbumsPage) onShowArtistPage(artistID string) {
a.nav(ArtistRoute(artistID))
}
func (a *AlbumsPage) onSortOrderChanged(order string) { func (a *AlbumsPage) onSortOrderChanged(order string) {
a.grid.Reset(a.lm.AlbumsIter(backend.AlbumSortOrder(order))) a.grid.Reset(a.lm.AlbumsIter(backend.AlbumSortOrder(order)))
if a.searchText == "" { if a.searchText == "" {
+73
View File
@@ -0,0 +1,73 @@
package ui
import (
"log"
"supersonic/backend"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
var _ fyne.Widget = (*ArtistPage)(nil)
type ArtistPage struct {
widget.BaseWidget
artistID string
im *backend.ImageManager
sm *backend.ServerManager
grid *AlbumGrid
titleDisp *widget.RichText
container *fyne.Container
OnPlayAlbum func(string)
}
func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager) *ArtistPage {
a := &ArtistPage{
artistID: artistID,
sm: sm,
im: im,
}
a.ExtendBaseWidget(a)
a.titleDisp = widget.NewRichTextWithText("Artist")
a.titleDisp.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{
SizeName: theme.SizeNameHeadingText,
}
a.container = container.NewBorder(a.titleDisp, nil, nil, nil, layout.NewSpacer())
a.loadAsync()
return a
}
func (a *ArtistPage) SetPlayAlbumCallback(cb func(string)) {
a.OnPlayAlbum = cb
}
func (a *ArtistPage) onPlayAlbum(albumID string) {
if a.OnPlayAlbum != nil {
a.OnPlayAlbum(albumID)
}
}
func (a *ArtistPage) loadAsync() {
go func() {
artist, err := a.sm.Server.GetArtist(a.artistID)
if err != nil {
log.Printf("Failed to get artist: %s", err.Error())
return
}
a.titleDisp.Segments[0].(*widget.TextSegment).Text = artist.Name
a.titleDisp.Refresh()
ag := NewFixedAlbumGrid(artist.Album, a.im.GetAlbumThumbnail, true /*showYear*/)
ag.OnPlayAlbum = a.onPlayAlbum
a.container.Objects[0] = ag
a.container.Refresh()
}()
}
func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer {
a.ExtendBaseWidget(a)
return widget.NewSimpleRenderer(a.container)
}
+52 -4
View File
@@ -1,12 +1,14 @@
package ui package ui
import ( import (
"supersonic/backend"
"supersonic/ui/widgets" "supersonic/ui/widgets"
"sync" "sync"
"time" "time"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
@@ -18,14 +20,26 @@ type Searchable interface {
OnSearched(string) OnSearched(string)
} }
type CanPlayAlbum interface {
SetPlayAlbumCallback(func(albumID string))
}
type BrowsingPane struct { type BrowsingPane struct {
widget.BaseWidget widget.BaseWidget
app *backend.App
curPage Page
forward *widget.Button
back *widget.Button
history []Page
historyIdx int
searchBar *widgets.SearchEntry searchBar *widgets.SearchEntry
pendingSearchLock sync.Mutex pendingSearchLock sync.Mutex
pendingSearch bool pendingSearch bool
searchGoroutine bool searchGoroutine bool
curPage Page
container *fyne.Container container *fyne.Container
} }
@@ -34,24 +48,58 @@ type blankPage struct {
widget.Separator widget.Separator
} }
func NewBrowsingPane() *BrowsingPane { func NewBrowsingPane(app *backend.App) *BrowsingPane {
b := &BrowsingPane{} b := &BrowsingPane{app: app}
b.ExtendBaseWidget(b) b.ExtendBaseWidget(b)
b.searchBar = widgets.NewSearchEntry() b.searchBar = widgets.NewSearchEntry()
b.searchBar.OnTextChanged = b.onSearchTextChanged b.searchBar.OnTextChanged = b.onSearchTextChanged
b.back = widget.NewButtonWithIcon("", theme.NavigateBackIcon(), b.GoBack)
b.forward = widget.NewButtonWithIcon("", theme.NavigateNextIcon(), b.GoForward)
b.curPage = &blankPage{} b.curPage = &blankPage{}
b.container = container.NewBorder( b.container = container.NewBorder(
container.NewHBox(widgets.NewHSpace(15), b.searchBar), container.NewHBox(b.back, b.forward, b.searchBar),
nil, nil, nil, b.curPage) nil, nil, nil, b.curPage)
return b return b
} }
func (b *BrowsingPane) SetPage(p Page) { func (b *BrowsingPane) SetPage(p Page) {
b.addPageToHistory(p)
b.doSetPage(p)
}
func (b *BrowsingPane) doSetPage(p Page) {
b.curPage = p b.curPage = p
if pa, ok := p.(CanPlayAlbum); ok {
pa.SetPlayAlbumCallback(func(albumID string) {
_ = b.app.PlaybackManager.PlayAlbum(albumID)
})
}
_, s := p.(Searchable)
b.searchBar.Hidden = !s
b.container.Objects[0] = p b.container.Objects[0] = p
b.Refresh() b.Refresh()
} }
func (b *BrowsingPane) addPageToHistory(p Page) {
b.history = b.history[:b.historyIdx]
b.history = append(b.history, p)
b.historyIdx++
}
func (b *BrowsingPane) GoBack() {
if b.historyIdx > 1 {
b.historyIdx -= 1
b.doSetPage(b.history[b.historyIdx-1])
}
}
func (b *BrowsingPane) GoForward() {
if b.historyIdx < len(b.history) {
b.historyIdx++
b.doSetPage(b.history[b.historyIdx-1])
}
}
func (b *BrowsingPane) onSearchTextChanged(text string) { func (b *BrowsingPane) onSearchTextChanged(text string) {
if text == "" { if text == "" {
b.sendSearch("") b.sendSearch("")
+4 -6
View File
@@ -13,6 +13,7 @@ import (
type MainWindow struct { type MainWindow struct {
Window fyne.Window Window fyne.Window
Router Router
BrowsingPane *BrowsingPane BrowsingPane *BrowsingPane
BottomPanel *BottomPanel BottomPanel *BottomPanel
@@ -22,9 +23,10 @@ type MainWindow struct {
func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App) MainWindow { func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App) MainWindow {
m := MainWindow{ m := MainWindow{
Window: fyneApp.NewWindow(appName), Window: fyneApp.NewWindow(appName),
BrowsingPane: NewBrowsingPane(), BrowsingPane: NewBrowsingPane(app),
BottomPanel: NewBottomPanel(app.Player), BottomPanel: NewBottomPanel(app.Player),
} }
m.Router = NewRouter(app, m.BrowsingPane)
m.BottomPanel.SetPlaybackManager(app.PlaybackManager) m.BottomPanel.SetPlaybackManager(app.PlaybackManager)
m.BottomPanel.ImageManager = app.ImageManager m.BottomPanel.ImageManager = app.ImageManager
m.container = container.NewBorder(nil, m.BottomPanel, nil, nil, m.BrowsingPane) m.container = container.NewBorder(nil, m.BottomPanel, nil, nil, m.BrowsingPane)
@@ -38,11 +40,7 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App) MainWindo
m.Window.SetTitle(song.Title) m.Window.SetTitle(song.Title)
}) })
app.ServerManager.OnServerConnected(func() { app.ServerManager.OnServerConnected(func() {
ap := NewAlbumsPage("Albums", app.LibraryManager, app.ImageManager) m.Router.OpenRoute(AlbumsRoute(backend.AlbumSortRecentlyAdded))
ap.OnPlayAlbum = func(albumID string) {
_ = app.PlaybackManager.PlayAlbum(albumID)
}
m.BrowsingPane.SetPage(ap)
}) })
return m return m
} }
+58
View File
@@ -0,0 +1,58 @@
package ui
import "supersonic/backend"
type PageName int
const (
Blank PageName = iota
Album
Albums
Artist
Artists
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}
}
type NavigationHandler interface {
SetPage(Page)
}
type Router struct {
App *backend.App
Nav NavigationHandler
}
func NewRouter(app *backend.App, nav NavigationHandler) Router {
return Router{
App: app,
Nav: nav,
}
}
func (r Router) CreatePage(rte Route) Page {
switch rte.Page {
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)
}
return nil
}
func (r Router) OpenRoute(rte Route) {
r.Nav.SetPage(r.CreatePage(rte))
}
+29 -6
View File
@@ -3,6 +3,7 @@ package widgets
import ( import (
"context" "context"
"image" "image"
"strconv"
"supersonic/ui/layout" "supersonic/ui/layout"
@@ -62,10 +63,14 @@ type AlbumCard struct {
widget.BaseWidget widget.BaseWidget
albumID string albumID string
artistID string
title *widget.Label title *widget.Label
artist *widget.Label artist *CustomHyperlink
year *widget.Label
container *fyne.Container container *fyne.Container
showYear bool
// updated by AlbumGrid // updated by AlbumGrid
Cover *albumCover Cover *albumCover
@@ -74,6 +79,8 @@ type AlbumCard struct {
ImgLoadCancel context.CancelFunc ImgLoadCancel context.CancelFunc
OnPlay func() OnPlay func()
OnShowAlbumPage func()
OnShowArtistPage func()
} }
func (a *AlbumCard) MouseIn(*desktop.MouseEvent) {} func (a *AlbumCard) MouseIn(*desktop.MouseEvent) {}
@@ -82,11 +89,13 @@ func (a *AlbumCard) MouseOut() {}
func (a *AlbumCard) MouseMoved(*desktop.MouseEvent) {} func (a *AlbumCard) MouseMoved(*desktop.MouseEvent) {}
func NewAlbumCard() *AlbumCard { func NewAlbumCard(showYear bool) *AlbumCard {
a := &AlbumCard{ a := &AlbumCard{
title: widget.NewLabel(""), title: widget.NewLabel(""),
artist: widget.NewLabel(""), artist: NewCustomHyperlink(),
year: widget.NewLabel(""),
Cover: newAlbumCover(), Cover: newAlbumCover(),
showYear: showYear,
} }
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.Cover.OnDoubleTapped = func() { a.Cover.OnDoubleTapped = func() {
@@ -94,8 +103,12 @@ func NewAlbumCard() *AlbumCard {
a.OnPlay() a.OnPlay()
} }
} }
a.artist.OnTapped = func() {
if a.OnShowArtistPage != nil {
a.OnShowArtistPage()
}
}
a.title.Wrapping = fyne.TextTruncate a.title.Wrapping = fyne.TextTruncate
a.artist.Wrapping = fyne.TextTruncate
a.title.TextStyle = fyne.TextStyle{Bold: true} a.title.TextStyle = fyne.TextStyle{Bold: true}
a.createContainer() a.createContainer()
@@ -103,8 +116,12 @@ func NewAlbumCard() *AlbumCard {
} }
func (a *AlbumCard) createContainer() { func (a *AlbumCard) createContainer() {
titleArtist := container.New(&layout.VboxCustomPadding{ExtraPad: -16}, a.title, a.artist) var secondLabel fyne.Widget = a.artist
c := container.New(&layout.VboxCustomPadding{ExtraPad: -5}, a.Cover, titleArtist) if a.showYear {
secondLabel = a.year
}
info := container.New(&layout.VboxCustomPadding{ExtraPad: -16}, a.title, secondLabel)
c := container.New(&layout.VboxCustomPadding{ExtraPad: -5}, a.Cover, info)
pad := &layout.CenterPadLayout{PadLeftRight: 20, PadTopBottom: 10} pad := &layout.CenterPadLayout{PadLeftRight: 20, PadTopBottom: 10}
a.container = container.New(pad, c) a.container = container.New(pad, c)
} }
@@ -112,13 +129,19 @@ func (a *AlbumCard) createContainer() {
func (a *AlbumCard) Update(al *subsonic.AlbumID3) { func (a *AlbumCard) Update(al *subsonic.AlbumID3) {
a.title.SetText(al.Name) a.title.SetText(al.Name)
a.artist.SetText(al.Artist) a.artist.SetText(al.Artist)
a.year.SetText(strconv.Itoa(al.Year))
a.albumID = al.ID a.albumID = al.ID
a.artistID = al.ArtistID
} }
func (a *AlbumCard) AlbumID() string { func (a *AlbumCard) AlbumID() string {
return a.albumID return a.albumID
} }
func (a *AlbumCard) ArtistID() string {
return a.artistID
}
func (a *AlbumCard) CreateRenderer() fyne.WidgetRenderer { func (a *AlbumCard) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(a.container) return widget.NewSimpleRenderer(a.container)
} }
+80
View File
@@ -0,0 +1,80 @@
package widgets
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
type hyperlinkWrapper struct {
widget.BaseWidget
h *widget.Hyperlink
l *widget.Label
maxWidth float32
}
func newHyperlinkWrapper(maxWidth float32) *hyperlinkWrapper {
h := &hyperlinkWrapper{
h: widget.NewHyperlink("", nil),
l: widget.NewLabel(""),
maxWidth: maxWidth,
}
h.h.Wrapping = fyne.TextTruncate
h.ExtendBaseWidget(h)
return h
}
func (h *hyperlinkWrapper) MinSize() fyne.Size {
w := fyne.Min(h.maxWidth, h.l.MinSize().Width)
return fyne.NewSize(w, h.h.MinSize().Height)
}
func (h *hyperlinkWrapper) SetText(text string) {
h.h.SetText(text)
h.l.SetText(text)
}
func (h *hyperlinkWrapper) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(h.h)
}
type CustomHyperlink struct {
widget.BaseWidget
h *hyperlinkWrapper
OnTapped func()
container *fyne.Container
minSize fyne.Size
}
func NewCustomHyperlink() *CustomHyperlink {
c := &CustomHyperlink{
h: newHyperlinkWrapper(200),
}
c.h.h.OnTapped = func() {
if c.OnTapped != nil {
c.OnTapped()
}
}
c.ExtendBaseWidget(c)
c.container = container.NewHBox(c.h, layout.NewSpacer())
return c
}
func (c *CustomHyperlink) SetText(text string) {
s := widget.NewLabel(text).MinSize()
c.h.SetText(text)
c.minSize = fyne.NewSize(fyne.Min(c.Size().Width, s.Width), s.Height)
c.Refresh()
}
func (c *CustomHyperlink) Refresh() {
c.container.Refresh()
}
func (c *CustomHyperlink) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(c.container)
}