From 203070328cf60bd3bfdef67d3ff4fd82bfe464ba Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 9 Jul 2023 14:14:23 -0700 Subject: [PATCH] reuse grid view across several pages --- ui/browsing/albumpage.go | 2 +- ui/browsing/albumspage.go | 26 ++++++++++++++++++++++---- ui/browsing/artistpage.go | 12 +++++++++++- ui/browsing/artistspage.go | 20 +++++++++++++++++--- ui/browsing/genrepage.go | 29 ++++++++++++++++++++++++----- ui/browsing/playlistspage.go | 29 +++++++++++++++++++++++------ ui/browsing/router.go | 8 ++++---- ui/util/widgetpool.go | 1 + ui/widgets/gridview.go | 9 +++++---- ui/widgets/gridviewitem.go | 4 ++++ ui/widgets/imageplaceholder.go | 6 ++++-- 11 files changed, 116 insertions(+), 30 deletions(-) diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 972f6d3..97b39e4 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -78,7 +78,7 @@ func newAlbumPage( }, } a.ExtendBaseWidget(a) - if h := a.pool.Obtain(util.WidgetTypeAlbumPageHeader); h != nil { + if h := pool.Obtain(util.WidgetTypeAlbumPageHeader); h != nil { a.header = h.(*AlbumPageHeader) a.header.Clear() } else { diff --git a/ui/browsing/albumspage.go b/ui/browsing/albumspage.go index e859b65..6cd3479 100644 --- a/ui/browsing/albumspage.go +++ b/ui/browsing/albumspage.go @@ -22,6 +22,7 @@ type AlbumsPage struct { widget.BaseWidget cfg *backend.AlbumsPageConfig + pool *util.WidgetPool contr *controller.Controller pm *backend.PlaybackManager im *backend.ImageManager @@ -57,8 +58,9 @@ func (s *selectWidget) MinSize() fyne.Size { return fyne.NewSize(170, s.Select.MinSize().Height) } -func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager) *AlbumsPage { +func NewAlbumsPage(cfg *backend.AlbumsPageConfig, pool *util.WidgetPool, contr *controller.Controller, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager) *AlbumsPage { a := &AlbumsPage{ + pool: pool, cfg: cfg, contr: contr, pm: pm, @@ -77,7 +79,13 @@ func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller, } a.sortOrder.Selected = cfg.SortOrder iter := mp.IterateAlbums(a.sortOrder.Selected, a.filter) - a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im, myTheme.AlbumIcon) + if g := pool.Obtain(util.WidgetTypeGridView); g != nil { + a.grid = g.(*widgets.GridView) + a.grid.Placeholder = myTheme.AlbumIcon + a.grid.Reset(widgets.NewGridViewAlbumIterator(iter)) + } else { + a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im, myTheme.AlbumIcon) + } contr.ConnectAlbumGridActions(a.grid) a.createSearchAndFilter() a.createContainer() @@ -108,6 +116,7 @@ func (a *AlbumsPage) createContainer() { func restoreAlbumsPage(saved *savedAlbumsPage) *AlbumsPage { a := &AlbumsPage{ cfg: saved.cfg, + pool: saved.pool, contr: saved.contr, pm: saved.pm, mp: saved.mp, @@ -126,11 +135,16 @@ func restoreAlbumsPage(saved *savedAlbumsPage) *AlbumsPage { a.sortOrder = NewSelect(a.mp.AlbumSortOrders(), nil) a.sortOrder.Selected = saved.sortOrder a.sortOrder.OnChanged = a.onSortOrderChanged + state := saved.gridState if a.searchText != "" { a.sortOrder.Disable() - a.grid = widgets.NewGridViewFromState(saved.searchGridState) + state = saved.searchGridState + } + if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil { + a.grid = g.(*widgets.GridView) + a.grid.ResetFromState(state) } else { - a.grid = widgets.NewGridViewFromState(saved.gridState) + a.grid = widgets.NewGridViewFromState(state) } a.createSearchAndFilter() a.createContainer() @@ -172,6 +186,7 @@ func (a *AlbumsPage) Reload() { func (a *AlbumsPage) Save() SavedPage { sa := &savedAlbumsPage{ cfg: a.cfg, + pool: a.pool, contr: a.contr, pm: a.pm, mp: a.mp, @@ -187,6 +202,8 @@ func (a *AlbumsPage) Save() SavedPage { } else { sa.searchGridState = a.grid.SaveToState() } + a.grid.Clear() + a.pool.Release(util.WidgetTypeGridView, a.grid) return sa } @@ -213,6 +230,7 @@ type savedAlbumsPage struct { searchText string filter mediaprovider.AlbumFilter cfg *backend.AlbumsPageConfig + pool *util.WidgetPool contr *controller.Controller pm *backend.PlaybackManager mp mediaprovider.MediaProvider diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index 8290e1b..3132b43 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -129,6 +129,10 @@ func (a *ArtistPage) Save() SavedPage { a.pool.Release(util.WidgetTypeTracklist, tl) } a.pool.Release(util.WidgetTypeArtistPageHeader, a.header) + if a.albumGrid != nil { + a.albumGrid.Clear() + a.pool.Release(util.WidgetTypeGridView, a.albumGrid) + } return &s } @@ -186,7 +190,13 @@ func (a *ArtistPage) showAlbumGrid() { Secondary: strconv.Itoa(al.Year), } }) - a.albumGrid = widgets.NewFixedGridView(model, a.im, myTheme.AlbumIcon) + if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil { + a.albumGrid = g.(*widgets.GridView) + a.albumGrid.Placeholder = myTheme.AlbumIcon + a.albumGrid.ResetFixed(model) + } else { + a.albumGrid = widgets.NewFixedGridView(model, a.im, myTheme.AlbumIcon) + } a.contr.ConnectAlbumGridActions(a.albumGrid) } a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid diff --git a/ui/browsing/artistspage.go b/ui/browsing/artistspage.go index 7e770fa..144f52a 100644 --- a/ui/browsing/artistspage.go +++ b/ui/browsing/artistspage.go @@ -23,6 +23,7 @@ type ArtistsPage struct { widget.BaseWidget contr *controller.Controller + pool *util.WidgetPool im *backend.ImageManager pm *backend.PlaybackManager mp mediaprovider.MediaProvider @@ -40,15 +41,17 @@ type ArtistsPage struct { func NewArtistsPage( contr *controller.Controller, + pool *util.WidgetPool, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, ) *ArtistsPage { - return newArtistsPage(contr, pm, mp, im, "", 0, 0) + return newArtistsPage(contr, pool, pm, mp, im, "", 0, 0) } func newArtistsPage( contr *controller.Controller, + pool *util.WidgetPool, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, @@ -58,6 +61,7 @@ func newArtistsPage( ) *ArtistsPage { a := &ArtistsPage{ contr: contr, + pool: pool, pm: pm, mp: mp, im: im, @@ -74,7 +78,13 @@ func newArtistsPage( a.searcher = widgets.NewSearchEntry() a.searcher.OnSearched = func(query string) { a.onSearched(query, false /*firstLoad*/) } a.searcher.Entry.Text = searchText - a.grid = widgets.NewFixedGridView(nil, a.im, myTheme.ArtistIcon) + if g := pool.Obtain(util.WidgetTypeGridView); g != nil { + a.grid = g.(*widgets.GridView) + a.grid.Placeholder = myTheme.ArtistIcon + a.grid.Clear() + } else { + a.grid = widgets.NewFixedGridView(nil, a.im, myTheme.ArtistIcon) + } a.contr.ConnectArtistGridActions(a.grid) searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher, layout.NewSpacer()) @@ -158,6 +168,7 @@ func (a *ArtistsPage) Route() controller.Route { func (a *ArtistsPage) Save() SavedPage { s := &savedArtistsPage{ contr: a.contr, + pool: a.pool, im: a.im, pm: a.pm, mp: a.mp, @@ -169,11 +180,14 @@ func (a *ArtistsPage) Save() SavedPage { } else { s.searchGridScrollPos = a.grid.GetScrollOffset() } + a.grid.Clear() + a.pool.Release(util.WidgetTypeGridView, a.grid) return s } type savedArtistsPage struct { contr *controller.Controller + pool *util.WidgetPool im *backend.ImageManager pm *backend.PlaybackManager mp mediaprovider.MediaProvider @@ -183,5 +197,5 @@ type savedArtistsPage struct { } func (s *savedArtistsPage) Restore() Page { - return newArtistsPage(s.contr, s.pm, s.mp, s.im, s.searchText, s.fullGridScrollPos, s.searchGridScrollPos) + return newArtistsPage(s.contr, s.pool, s.pm, s.mp, s.im, s.searchText, s.fullGridScrollPos, s.searchGridScrollPos) } diff --git a/ui/browsing/genrepage.go b/ui/browsing/genrepage.go index 68c90f6..48b0275 100644 --- a/ui/browsing/genrepage.go +++ b/ui/browsing/genrepage.go @@ -20,6 +20,7 @@ type GenrePage struct { widget.BaseWidget genre string + pool *util.WidgetPool contr *controller.Controller im *backend.ImageManager pm *backend.PlaybackManager @@ -37,9 +38,10 @@ type GenrePage struct { container *fyne.Container } -func NewGenrePage(genre string, contr *controller.Controller, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager) *GenrePage { +func NewGenrePage(genre string, pool *util.WidgetPool, contr *controller.Controller, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager) *GenrePage { g := &GenrePage{ genre: genre, + pool: pool, filter: mediaprovider.AlbumFilter{Genres: []string{genre}}, contr: contr, pm: pm, @@ -53,8 +55,14 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba SizeName: theme.SizeNameHeadingText, } g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs) - iter := g.mp.IterateAlbums("", g.filter) - g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im, myTheme.AlbumIcon) + iter := widgets.NewGridViewAlbumIterator(g.mp.IterateAlbums("", g.filter)) + if gv := pool.Obtain(util.WidgetTypeGridView); gv != nil { + g.grid = gv.(*widgets.GridView) + g.grid.Placeholder = myTheme.AlbumIcon + g.grid.Reset(iter) + } else { + g.grid = widgets.NewGridView(iter, g.im, myTheme.AlbumIcon) + } g.contr.ConnectAlbumGridActions(g.grid) g.createSearchAndFilter() g.createContainer() @@ -85,6 +93,7 @@ func (g *GenrePage) createContainer() { func restoreGenrePage(saved *savedGenrePage) *GenrePage { g := &GenrePage{ genre: saved.genre, + pool: saved.pool, contr: saved.contr, pm: saved.pm, mp: saved.mp, @@ -101,10 +110,16 @@ func restoreGenrePage(saved *savedGenrePage) *GenrePage { SizeName: theme.SizeNameHeadingText, } g.playRandom = widget.NewButtonWithIcon(" Play random", myTheme.ShuffleIcon, g.playRandomSongs) + state := saved.gridState if g.searchText != "" { - g.grid = widgets.NewGridViewFromState(saved.searchGridState) + state = saved.searchGridState + } + if gv := g.pool.Obtain(util.WidgetTypeGridView); gv != nil { + g.grid = gv.(*widgets.GridView) + g.grid.Placeholder = myTheme.AlbumIcon + g.grid.ResetFromState(state) } else { - g.grid = widgets.NewGridViewFromState(saved.gridState) + g.grid = widgets.NewGridViewFromState(state) } g.createSearchAndFilter() g.createContainer() @@ -132,6 +147,7 @@ func (g *GenrePage) Reload() { func (g *GenrePage) Save() SavedPage { sg := &savedGenrePage{ genre: g.genre, + pool: g.pool, filter: g.filter, searchText: g.searchText, contr: g.contr, @@ -146,6 +162,8 @@ func (g *GenrePage) Save() SavedPage { } else { sg.gridState = g.grid.SaveToState() } + g.grid.Clear() + g.pool.Release(util.WidgetTypeGridView, g.grid) return sg } @@ -179,6 +197,7 @@ func (g *GenrePage) playRandomSongs() { type savedGenrePage struct { genre string searchText string + pool *util.WidgetPool filter mediaprovider.AlbumFilter contr *controller.Controller pm *backend.PlaybackManager diff --git a/ui/browsing/playlistspage.go b/ui/browsing/playlistspage.go index 46869c5..e88444e 100644 --- a/ui/browsing/playlistspage.go +++ b/ui/browsing/playlistspage.go @@ -13,6 +13,7 @@ import ( "github.com/dweymouth/supersonic/ui/controller" "github.com/dweymouth/supersonic/ui/layouts" myTheme "github.com/dweymouth/supersonic/ui/theme" + "github.com/dweymouth/supersonic/ui/util" "github.com/dweymouth/supersonic/ui/widgets" "fyne.io/fyne/v2" @@ -25,6 +26,7 @@ import ( type PlaylistsPage struct { widget.BaseWidget + pool *util.WidgetPool cfg *backend.PlaylistsPageConfig contr *controller.Controller mp mediaprovider.MediaProvider @@ -39,16 +41,17 @@ type PlaylistsPage struct { gridView *widgets.GridView } -func NewPlaylistsPage(contr *controller.Controller, cfg *backend.PlaylistsPageConfig, mp mediaprovider.MediaProvider) *PlaylistsPage { +func NewPlaylistsPage(contr *controller.Controller, pool *util.WidgetPool, cfg *backend.PlaylistsPageConfig, mp mediaprovider.MediaProvider) *PlaylistsPage { activeView := 0 if cfg.InitialView == "Grid" { activeView = 1 } - return newPlaylistsPage(contr, cfg, mp, "", activeView) + return newPlaylistsPage(contr, pool, cfg, mp, "", activeView) } -func newPlaylistsPage(contr *controller.Controller, cfg *backend.PlaylistsPageConfig, mp mediaprovider.MediaProvider, searchText string, activeView int) *PlaylistsPage { +func newPlaylistsPage(contr *controller.Controller, pool *util.WidgetPool, cfg *backend.PlaylistsPageConfig, mp mediaprovider.MediaProvider, searchText string, activeView int) *PlaylistsPage { a := &PlaylistsPage{ + pool: pool, cfg: cfg, mp: mp, contr: contr, @@ -95,7 +98,13 @@ func (a *PlaylistsPage) createListView() { func (a *PlaylistsPage) createGridView(playlists []*mediaprovider.Playlist) { model := createPlaylistGridViewModel(playlists) - a.gridView = widgets.NewFixedGridView(model, a.contr.App.ImageManager, myTheme.PlaylistIcon) + if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil { + a.gridView = g.(*widgets.GridView) + a.gridView.Placeholder = myTheme.PlaylistIcon + a.gridView.ResetFixed(model) + } else { + a.gridView = widgets.NewFixedGridView(model, a.contr.App.ImageManager, myTheme.PlaylistIcon) + } a.gridView.OnPlay = func(id string, shuffle bool) { go a.contr.App.PlaybackManager.PlayPlaylist(id, 0, shuffle) } @@ -103,6 +112,7 @@ func (a *PlaylistsPage) createGridView(playlists []*mediaprovider.Playlist) { go a.contr.App.PlaybackManager.LoadPlaylist(id, true, false) } a.gridView.OnShowItemPage = a.showPlaylistPage + a.gridView.OnShowSecondaryPage = nil a.gridView.OnAddToPlaylist = func(id string) { go func() { pl, err := a.contr.App.ServerManager.Server.GetPlaylist(id) @@ -221,17 +231,24 @@ func (a *PlaylistsPage) Reload() { } func (a *PlaylistsPage) Save() SavedPage { - return &savedPlaylistsPage{ + s := &savedPlaylistsPage{ contr: a.contr, + pool: a.pool, cfg: a.cfg, mp: a.mp, searchText: a.searcher.Entry.Text, activeView: a.viewToggle.ActivatedButtonIndex(), } + if a.gridView != nil { + a.gridView.Clear() + a.pool.Release(util.WidgetTypeGridView, a.gridView) + } + return s } type savedPlaylistsPage struct { contr *controller.Controller + pool *util.WidgetPool cfg *backend.PlaylistsPageConfig mp mediaprovider.MediaProvider searchText string @@ -239,7 +256,7 @@ type savedPlaylistsPage struct { } func (s *savedPlaylistsPage) Restore() Page { - return newPlaylistsPage(s.contr, s.cfg, s.mp, s.searchText, s.activeView) + return newPlaylistsPage(s.contr, s.pool, s.cfg, s.mp, s.searchText, s.activeView) } func (a *PlaylistsPage) buildContainer(initialView fyne.CanvasObject) { diff --git a/ui/browsing/router.go b/ui/browsing/router.go index 79ac547..67dfa99 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -33,15 +33,15 @@ func (r Router) CreatePage(rte controller.Route) Page { case controller.Album: return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, &r.widgetPool, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller) case controller.Albums: - return NewAlbumsPage(&r.App.Config.AlbumsPage, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) + return NewAlbumsPage(&r.App.Config.AlbumsPage, &r.widgetPool, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) case controller.Artist: return NewArtistPage(rte.Arg, &r.App.Config.ArtistPage, &r.widgetPool, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller) case controller.Artists: - return NewArtistsPage(r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) + return NewArtistsPage(r.Controller, &r.widgetPool, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) case controller.Favorites: return NewFavoritesPage(&r.App.Config.FavoritesPage, &r.widgetPool, r.Controller, r.App.ServerManager.Server, r.App.PlaybackManager, r.App.ImageManager) case controller.Genre: - return NewGenrePage(rte.Arg, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) + return NewGenrePage(rte.Arg, &r.widgetPool, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager) case controller.Genres: return NewGenresPage(r.Controller, r.App.ServerManager.Server) case controller.NowPlaying: @@ -49,7 +49,7 @@ func (r Router) CreatePage(rte controller.Route) Page { case controller.Playlist: return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, &r.widgetPool, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager) case controller.Playlists: - return NewPlaylistsPage(r.Controller, &r.App.Config.PlaylistsPage, r.App.ServerManager.Server) + return NewPlaylistsPage(r.Controller, &r.widgetPool, &r.App.Config.PlaylistsPage, r.App.ServerManager.Server) case controller.Tracks: return NewTracksPage(r.Controller, &r.App.Config.TracksPage, &r.widgetPool, r.App.ServerManager.Server) } diff --git a/ui/util/widgetpool.go b/ui/util/widgetpool.go index a65133d..ea73306 100644 --- a/ui/util/widgetpool.go +++ b/ui/util/widgetpool.go @@ -11,6 +11,7 @@ type WidgetType int const ( WidgetTypeAlbumPageHeader WidgetType = iota WidgetTypeArtistPageHeader + WidgetTypeGridView WidgetTypePlaylistPageHeader WidgetTypeTracklist diff --git a/ui/widgets/gridview.go b/ui/widgets/gridview.go index a0c957d..36ada94 100644 --- a/ui/widgets/gridview.go +++ b/ui/widgets/gridview.go @@ -82,7 +82,7 @@ type GridViewState struct { items []GridViewItemModel iter GridViewIterator imageFetcher ImageFetcher - placeholder fyne.Resource + Placeholder fyne.Resource highestShown int done bool @@ -104,7 +104,7 @@ func NewFixedGridView(items []GridViewItemModel, fetch ImageFetcher, placeholder items: items, done: true, imageFetcher: fetch, - placeholder: placeholder, + Placeholder: placeholder, }, } g.ExtendBaseWidget(g) @@ -117,7 +117,7 @@ func NewGridView(iter GridViewIterator, fetch ImageFetcher, placeholder fyne.Res GridViewState: GridViewState{ iter: iter, imageFetcher: fetch, - placeholder: placeholder, + Placeholder: placeholder, }, } g.ExtendBaseWidget(g) @@ -213,7 +213,7 @@ func (g *GridView) createGridWrap() { }, // create func func() fyne.CanvasObject { - card := NewGridViewItem(g.placeholder) + card := NewGridViewItem(g.Placeholder) card.OnPlay = func(shuffle bool) { if g.OnPlay != nil { g.OnPlay(card.ItemID(), shuffle) @@ -266,6 +266,7 @@ func (g *GridView) doUpdateItemCard(itemIdx int, card *GridViewItem) { item = g.items[itemIdx] } g.stateMutex.RUnlock() + card.Cover.Im.CenterIcon = g.Placeholder if card.PrevID == item.ID { // nothing to do return diff --git a/ui/widgets/gridviewitem.go b/ui/widgets/gridviewitem.go index edd3cba..4792479 100644 --- a/ui/widgets/gridviewitem.go +++ b/ui/widgets/gridviewitem.go @@ -191,6 +191,10 @@ func (g *GridViewItem) Update(model GridViewItemModel) { g.Cover.playbtn.Hidden = true } +func (g *GridViewItem) Refresh() { + g.BaseWidget.Refresh() +} + func (g *GridViewItem) ItemID() string { return g.itemID } diff --git a/ui/widgets/imageplaceholder.go b/ui/widgets/imageplaceholder.go index 4d0de6e..531d64c 100644 --- a/ui/widgets/imageplaceholder.go +++ b/ui/widgets/imageplaceholder.go @@ -15,7 +15,8 @@ import ( // a placeholder with a rectangular border frame // and an icon positioned in the center of the frame. type ImagePlaceholder struct { - ScaleMode canvas.ImageScale + ScaleMode canvas.ImageScale + CenterIcon fyne.Resource widget.BaseWidget content *fyne.Container @@ -30,7 +31,7 @@ type ImagePlaceholder struct { } func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceholder { - i := &ImagePlaceholder{minSize: minSize} + i := &ImagePlaceholder{minSize: minSize, CenterIcon: centerIcon} i.ExtendBaseWidget(i) i.iconImage = canvas.NewImageFromResource(centerIcon) i.iconImage.FillMode = canvas.ImageFillContain @@ -86,6 +87,7 @@ func (i *ImagePlaceholder) MinSize() fyne.Size { func (i *ImagePlaceholder) Refresh() { i.border.Hidden = i.HaveImage() + i.iconImage.Resource = i.CenterIcon i.iconImage.Hidden = i.HaveImage() i.imageDisp.Hidden = !i.HaveImage() i.imageDisp.ScaleMode = i.ScaleMode