Merge pull request #654 from dweymouth/feat/group-releases
Group by release type on artist page
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
"Close to system tray": "In Taskleiste minimieren",
|
||||
"Comment": "Kommentar",
|
||||
"Compilation": "Compilation",
|
||||
"Compilations": "Compilations",
|
||||
"Composer": "Komponist",
|
||||
"Configure your music server to add radio stations": "Konfigurieren Sie Ihren Musik-Server, um Radiosender hinzuzufügen",
|
||||
"Confirm Delete Playlist": "Playlist löschen bestätigen",
|
||||
@@ -68,6 +69,7 @@
|
||||
"Enabled": "Aktiviert",
|
||||
"Enter": "Eingeben",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Equalizer",
|
||||
"Exclusive mode": "Exklusiver Modus",
|
||||
"Favorites": "Favoriten",
|
||||
@@ -178,6 +180,7 @@
|
||||
"Shuffle tracks": "Titel zufällig wiedergeben",
|
||||
"Similar artists": "Ähnliche Künstler",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Größe",
|
||||
"Skip duplicate tracks": "Duplikate überspringen",
|
||||
"Skip this version": "Diese Version überspringen",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "Close to system tray",
|
||||
"Comment": "Comment",
|
||||
"Compilation": "Compilation",
|
||||
"Compilations": "Compilations",
|
||||
"Composer": "Composer",
|
||||
"Configure your music server to add radio stations": "Configure your music server to add radio stations",
|
||||
"Confirm Delete Playlist": "Confirm Delete Playlist",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "Enabled",
|
||||
"Enter": "Enter",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Equalizer",
|
||||
"Error": "Error",
|
||||
"Exclusive mode": "Exclusive mode",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "Shuffle tracks",
|
||||
"Similar artists": "Similar artists",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Size",
|
||||
"Skip duplicate tracks": "Skip duplicate tracks",
|
||||
"Skip SSL certificate verification": "Skip SSL certificate verification",
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"Close to system tray": "Cerrar a la bandeja del sistema",
|
||||
"Comment": "Comentario",
|
||||
"Compilation": "Compilación",
|
||||
"Compilations": "Compilaciónes",
|
||||
"Composer": "Compositor",
|
||||
"Configure your music server to add radio stations": "Configura tu servidor de música para agregar estaciones de radio",
|
||||
"Confirm Delete Playlist": "Confirmar eliminación de la lista de reproducción",
|
||||
@@ -69,6 +70,7 @@
|
||||
"Enabled": "Habilitado",
|
||||
"Enter": "Ingresar",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Ecualizador",
|
||||
"Error": "Error",
|
||||
"Exclusive mode": "Modo exclusivo",
|
||||
@@ -182,6 +184,7 @@
|
||||
"Shuffle tracks": "Aleatorio pistas",
|
||||
"Similar artists": "Artistas similares",
|
||||
"Single": "Sencillo",
|
||||
"Singles": "Sencillos",
|
||||
"Size": "Tamaño",
|
||||
"Skip duplicate tracks": "Saltar pistas duplicadas",
|
||||
"Skip this version": "Saltar esta versión",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "Réduire dans la barre d'état à la fermeture",
|
||||
"Comment": "Commentaire",
|
||||
"Compilation": "Compilation",
|
||||
"Compilations": "Compilations",
|
||||
"Composer": "Compositeur",
|
||||
"Configure your music server to add radio stations": "Configurez votre serveur de musique pour ajouter des stations de radio",
|
||||
"Confirm Delete Playlist": "Confirmer la suppression de la liste de lecture",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "Activé",
|
||||
"Enter": "Entrée",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Égaliseur",
|
||||
"Error": "Erreur",
|
||||
"Exclusive mode": "Mode exclusif",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "Mélanger les pistes",
|
||||
"Similar artists": "Artistes similaires",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Taille",
|
||||
"Skip duplicate tracks": "Ne pas lire les doublons de pistes",
|
||||
"Skip SSL certificate verification": "Ne pas vérifier le certificat SSL",
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"Close to system tray": "Chiudi nella barra di sistema",
|
||||
"Comment": "Commento",
|
||||
"Compilation": "Compilation",
|
||||
"Compilations": "Compilations",
|
||||
"Composer": "Compositore",
|
||||
"Configure your music server to add radio stations": "Configura il tuo server per aggiungere le stazioni radio",
|
||||
"Confirm Delete Playlist": "Eliminare la playlist?",
|
||||
@@ -68,6 +69,7 @@
|
||||
"Enabled": "Abilita",
|
||||
"Enter": "Invio",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Equalizzatore",
|
||||
"Exclusive mode": "Modalità esclusiva",
|
||||
"Favorites": "Preferiti",
|
||||
@@ -177,6 +179,7 @@
|
||||
"Shuffle tracks": "Tracce casuali",
|
||||
"Similar artists": "Artisti simili",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Dimensione",
|
||||
"Skip duplicate tracks": "Salta tracce duplicate",
|
||||
"Skip this version": "Salta questa versione",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "システムトレイに閉じる",
|
||||
"Comment": "コメント",
|
||||
"Compilation": "コンピレーション",
|
||||
"Compilations": "コンピレーション",
|
||||
"Composer": "作曲家",
|
||||
"Configure your music server to add radio stations": "音楽サーバーを設定してラジオ局を追加する",
|
||||
"Confirm Delete Playlist": "プレイリストの削除を確認",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "有効",
|
||||
"Enter": "入力",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "イコライザー",
|
||||
"Error": "エラー",
|
||||
"Exclusive mode": "排他モード",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "トラックをシャッフル",
|
||||
"Similar artists": "類似アーティスト",
|
||||
"Single": "シングル",
|
||||
"Singles": "シングル",
|
||||
"Size": "サイズ",
|
||||
"Skip duplicate tracks": "重複トラックをスキップ",
|
||||
"Skip SSL certificate verification": "SSL証明書の検証をスキップ",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "시스템 트레이로 최소화",
|
||||
"Comment": "코멘트",
|
||||
"Compilation": "컴필레이션",
|
||||
"Compilations": "컴필레이션",
|
||||
"Composer": "작곡가",
|
||||
"Configure your music server to add radio stations": "라디오 방송국을 추가하기 위해서는 음악 서버를 구성하세요",
|
||||
"Confirm Delete Playlist": "재생 목록 삭제 확인",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "활성화",
|
||||
"Enter": "입력",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "이퀄라이저",
|
||||
"Error": "오류",
|
||||
"Exclusive mode": "독점 모드",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "트랙 셔플하기",
|
||||
"Similar artists": "유사 아티스트",
|
||||
"Single": "싱글",
|
||||
"Singles": "싱글",
|
||||
"Size": "크기",
|
||||
"Skip duplicate tracks": "중복 트랙 건너뛰기",
|
||||
"Skip SSL certificate verification": "SSL 인증서 검증 건너뛰기",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"Close to system tray": "Sluiten naar systeemvak",
|
||||
"Comment": "Commentaar",
|
||||
"Compilation": "Compilatie",
|
||||
"Compilations": "Compilaties",
|
||||
"Composer": "Componist",
|
||||
"Configure your music server to add radio stations": "Configureer je muziekserver om radiostations toe te voegen",
|
||||
"Confirm Delete Playlist": "Bevestig afspeellijst wissen",
|
||||
@@ -79,6 +80,7 @@
|
||||
"Enabled": "Ingeschakeld",
|
||||
"Enter": "Enter",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Equalizer",
|
||||
"Error": "Fout",
|
||||
"Exclusive mode": "Exclusieve modus",
|
||||
@@ -193,6 +195,7 @@
|
||||
"Shuffle tracks": "Nummers door mekaar smijten",
|
||||
"Similar artists": "Gelijkaardige artiesten",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Grootte",
|
||||
"Skip duplicate tracks": "Dubbele nummers overslaan",
|
||||
"Skip SSL certificate verification": "SSL-certificaat verificatie overslaan",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"Close to system tray": "Zamknij do zasobnika systemowego",
|
||||
"Comment": "Komentarz",
|
||||
"Compilation": "Compilation",
|
||||
"Compilations": "Compilations",
|
||||
"Composer": "Kompozytor",
|
||||
"Configure your music server to add radio stations": "Skonfiguruj swój serwer muzyki aby dodać stacje radiowe",
|
||||
"Confirm Delete Playlist": "Potwierdź usunięcie playlisty",
|
||||
@@ -76,6 +77,7 @@
|
||||
"Enabled": "Włączony",
|
||||
"Enter": "Enter",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Equalizer",
|
||||
"Exclusive mode": "Tryb exclusive",
|
||||
"Favorites": "Ulubione",
|
||||
@@ -189,6 +191,7 @@
|
||||
"Shuffle tracks": "Wymieszaj ścieżki",
|
||||
"Similar artists": "Podobni artyści",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Wielkość",
|
||||
"Skip duplicate tracks": "Pomiń zduplikowane ścieżki",
|
||||
"Skip SSL certificate verification": "Pomiń weryfikację certyfikatu SSL",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"Close to system tray": "Fechar para a bandeja do sistema",
|
||||
"Comment": "Comentar",
|
||||
"Compilation": "Compilação",
|
||||
"Compilations": "Compilaçãoes",
|
||||
"Composer": "Compositor",
|
||||
"Configure your music server to add radio stations": "Configure o seu servidor de música para adicionar estações de rádio",
|
||||
"Confirm Delete Playlist": "Confirmar remoção da lista de reprodução",
|
||||
@@ -196,6 +197,7 @@
|
||||
"Shuffle tracks": "Faixas em ordem aleatória",
|
||||
"Similar artists": "Artistas similares",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Tamanho",
|
||||
"Skip duplicate tracks": "Pular faixas duplicadas",
|
||||
"Skip SSL certificate verification": "Pular verificação do certificado SSL",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"Close to system tray": "Închide în bara de sistem",
|
||||
"Comment": "Comentariu",
|
||||
"Compilation": "Compilație",
|
||||
"Compilations": "Compilații",
|
||||
"Composer": "Compozitor",
|
||||
"Configure your music server to add radio stations": "Configurează-ți serverul de muzică pentru a adăuga stații radio",
|
||||
"Confirm Delete Playlist": "Confirmare ștergere playlist",
|
||||
@@ -67,6 +68,7 @@
|
||||
"Enabled": "Activat",
|
||||
"Enter": "Enter",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Egalizator",
|
||||
"Exclusive mode": "Mod exclusiv",
|
||||
"Favorites": "Favorite",
|
||||
@@ -169,6 +171,7 @@
|
||||
"Shuffle tracks": "Amestecă melodii",
|
||||
"Similar artists": "Similar artists",
|
||||
"Single": "Single",
|
||||
"Singles": "Singles",
|
||||
"Size": "Mărime",
|
||||
"Skip duplicate tracks": "Sari peste melodii duplicate",
|
||||
"Skip this version": "Sari peste această versiune",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"Close to system tray": "Закрывать в область уведомлений",
|
||||
"Comment": "Комментарий",
|
||||
"Compilation": "Сборник",
|
||||
"Compilations": "сборники",
|
||||
"Composer": "Композитор",
|
||||
"Configure your music server to add radio stations": "Включите использование радиостанций на музыкальном сервере",
|
||||
"Confirm Delete Playlist": "Подтвердить удаление списка воспроизведения",
|
||||
@@ -82,6 +83,7 @@
|
||||
"Enabled": "Включен",
|
||||
"Enter": "Войти",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "Эквалайзер",
|
||||
"Error": "Ошибка",
|
||||
"Exclusive mode": "Исключительное использование",
|
||||
@@ -201,6 +203,7 @@
|
||||
"Shuffle tracks": "Перемешать композиции",
|
||||
"Similar artists": "Похожие исполнители",
|
||||
"Single": "Сингл",
|
||||
"Singles": "синглы",
|
||||
"Size": "Размер",
|
||||
"Skip duplicate tracks": "Не добавлять повторы",
|
||||
"Skip SSL certificate verification": "Не проверять сертификаты SSL",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "关闭到系统托盘",
|
||||
"Comment": "评论",
|
||||
"Compilation": "合集",
|
||||
"Compilations": "合集",
|
||||
"Composer": "作曲家",
|
||||
"Configure your music server to add radio stations": "配置您的音乐服务器以添加电台",
|
||||
"Confirm Delete Playlist": "确认删除播放列表",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "已启用",
|
||||
"Enter": "输入",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "均衡器",
|
||||
"Error": "错误",
|
||||
"Exclusive mode": "独占模式",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "随机播放曲目",
|
||||
"Similar artists": "相似艺术家",
|
||||
"Single": "单曲",
|
||||
"Singles": "单曲",
|
||||
"Size": "大小",
|
||||
"Skip duplicate tracks": "跳过重复曲目",
|
||||
"Skip SSL certificate verification": "跳过 SSL 证书验证",
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"Close to system tray": "关闭到系统托盘",
|
||||
"Comment": "评论",
|
||||
"Compilation": "合集",
|
||||
"Compilations": "合集",
|
||||
"Composer": "作曲家",
|
||||
"Configure your music server to add radio stations": "配置您的音乐服务器以添加电台",
|
||||
"Confirm Delete Playlist": "确认删除播放列表",
|
||||
@@ -81,6 +82,7 @@
|
||||
"Enabled": "已启用",
|
||||
"Enter": "输入",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "均衡器",
|
||||
"Error": "错误",
|
||||
"Exclusive mode": "独占模式",
|
||||
@@ -199,6 +201,7 @@
|
||||
"Shuffle tracks": "随机播放曲目",
|
||||
"Similar artists": "相似艺术家",
|
||||
"Single": "单曲",
|
||||
"Singles": "单曲",
|
||||
"Size": "大小",
|
||||
"Skip duplicate tracks": "跳过重复曲目",
|
||||
"Skip SSL certificate verification": "跳过 SSL 证书验证",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"Close to system tray": "關閉到系統匣",
|
||||
"Comment": "評論",
|
||||
"Compilation": "合輯",
|
||||
"Compilations": "合輯",
|
||||
"Composer": "作曲家",
|
||||
"Configure your music server to add radio stations": "配置您的音樂伺服器以新增電台",
|
||||
"Confirm Delete Playlist": "確認刪除播放清單",
|
||||
@@ -67,6 +68,7 @@
|
||||
"Enabled": "已啟用",
|
||||
"Enter": "輸入",
|
||||
"EP": "EP",
|
||||
"EPs": "EPs",
|
||||
"Equalizer": "均衡器",
|
||||
"Exclusive mode": "獨佔模式",
|
||||
"Favorites": "收藏",
|
||||
@@ -169,6 +171,7 @@
|
||||
"Shuffle tracks": "隨機播放曲目",
|
||||
"Similar artists": "相似藝術家",
|
||||
"Single": "單曲",
|
||||
"Singles": "單曲",
|
||||
"Skip duplicate tracks": "跳過重複曲目",
|
||||
"Skip this version": "跳過此版本",
|
||||
"Soundtrack": "原聲帶",
|
||||
|
||||
+148
-40
@@ -2,6 +2,7 @@ package browsing
|
||||
|
||||
import (
|
||||
"log"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
@@ -39,6 +40,12 @@ type artistPageState struct {
|
||||
mp mediaprovider.MediaProvider
|
||||
im *backend.ImageManager
|
||||
contr *controller.Controller
|
||||
|
||||
gridScrollPos float32 // for album grid (or grouped releases)
|
||||
listScrollPos float32 // for Top Tracks list
|
||||
|
||||
sectionVis widgets.GroupedReleasesSectionVisibility
|
||||
sectionVisNeedApply bool
|
||||
}
|
||||
|
||||
type ArtistPage struct {
|
||||
@@ -49,12 +56,13 @@ type ArtistPage struct {
|
||||
|
||||
artistInfo *mediaprovider.ArtistWithAlbums
|
||||
|
||||
albumGrid *widgets.GridView
|
||||
tracklistCtr *fyne.Container
|
||||
sortButton *widgets.SortChooserButton
|
||||
nowPlayingID string
|
||||
header *ArtistPageHeader
|
||||
container *fyne.Container
|
||||
albumGrid *widgets.GridView
|
||||
groupedReleases *widgets.GroupedReleases
|
||||
tracklistCtr *fyne.Container
|
||||
sortButton *widgets.SortChooserButton
|
||||
nowPlayingID string
|
||||
header *ArtistPageHeader
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -67,11 +75,7 @@ func NewArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.Wi
|
||||
if cfg.InitialView == viewTopTracks {
|
||||
activeView = 1
|
||||
}
|
||||
return newArtistPage(artistID, cfg, pool, pm, mp, im, contr, activeView, widgets.TracklistSort{})
|
||||
}
|
||||
|
||||
func newArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.WidgetPool, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, contr *controller.Controller, activeView int, sort widgets.TracklistSort) *ArtistPage {
|
||||
a := &ArtistPage{artistPageState: artistPageState{
|
||||
return newArtistPage(artistPageState{
|
||||
artistID: artistID,
|
||||
cfg: cfg,
|
||||
pool: pool,
|
||||
@@ -80,8 +84,11 @@ func newArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.Wi
|
||||
im: im,
|
||||
contr: contr,
|
||||
activeView: activeView,
|
||||
trackSort: sort,
|
||||
}}
|
||||
})
|
||||
}
|
||||
|
||||
func newArtistPage(state artistPageState) *ArtistPage {
|
||||
a := &ArtistPage{artistPageState: state}
|
||||
a.ExtendBaseWidget(a)
|
||||
if h := a.pool.Obtain(util.WidgetTypeArtistPageHeader); h != nil {
|
||||
a.header = h.(*ArtistPageHeader)
|
||||
@@ -90,7 +97,7 @@ func newArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.Wi
|
||||
a.header = NewArtistPageHeader(a)
|
||||
}
|
||||
a.header.artistPage = a
|
||||
if img, ok := im.GetCachedArtistImage(artistID); ok {
|
||||
if img, ok := state.im.GetCachedArtistImage(state.artistID); ok {
|
||||
a.header.artistImage.SetImage(img, true /*tappable*/)
|
||||
}
|
||||
viewToggle := widgets.NewToggleText(0, []string{lang.L("Discography"), lang.L("Top Tracks")})
|
||||
@@ -146,6 +153,7 @@ func (a *ArtistPage) Save() SavedPage {
|
||||
s := a.artistPageState
|
||||
if a.tracklistCtr != nil {
|
||||
tl := a.tracklistCtr.Objects[0].(*widgets.Tracklist)
|
||||
s.listScrollPos = tl.GetScrollOffset()
|
||||
s.trackSort = tl.Sorting()
|
||||
tl.Clear()
|
||||
a.pool.Release(util.WidgetTypeTracklist, tl)
|
||||
@@ -153,9 +161,17 @@ func (a *ArtistPage) Save() SavedPage {
|
||||
a.header.artistPage = nil
|
||||
a.pool.Release(util.WidgetTypeArtistPageHeader, a.header)
|
||||
if a.albumGrid != nil {
|
||||
s.gridScrollPos = a.albumGrid.GetScrollOffset()
|
||||
a.albumGrid.Clear()
|
||||
a.pool.Release(util.WidgetTypeGridView, a.albumGrid)
|
||||
}
|
||||
if a.groupedReleases != nil {
|
||||
s.gridScrollPos = a.groupedReleases.GetScrollOffset()
|
||||
s.sectionVis = a.groupedReleases.GetSectionVisibility()
|
||||
s.sectionVisNeedApply = true
|
||||
a.groupedReleases.Model = widgets.GroupedReleasesModel{}
|
||||
a.pool.Release(util.WidgetTypeGroupedReleases, a.groupedReleases)
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
@@ -175,6 +191,11 @@ var _ Scrollable = (*ArtistPage)(nil)
|
||||
func (g *ArtistPage) Scroll(scrollAmt float32) {
|
||||
if g.activeView == 0 && g.albumGrid != nil {
|
||||
g.albumGrid.ScrollToOffset(g.albumGrid.GetScrollOffset() + scrollAmt)
|
||||
} else if g.activeView == 0 && g.groupedReleases != nil {
|
||||
g.groupedReleases.ScrollToOffset(g.groupedReleases.GetScrollOffset() + scrollAmt)
|
||||
} else if g.activeView == 1 && g.tracklistCtr != nil {
|
||||
tl := g.tracklistCtr.Objects[0].(*widgets.Tracklist)
|
||||
tl.ScrollBy(scrollAmt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,31 +215,73 @@ func (a *ArtistPage) getGridViewAlbumsModel() []widgets.GridViewItemModel {
|
||||
if a.artistInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
a.sortAlbumsSlices(a.artistInfo.Albums)
|
||||
return sharedutil.MapSlice(a.artistInfo.Albums, a.albumToGridViewItemModel)
|
||||
}
|
||||
|
||||
func (a *ArtistPage) albumToGridViewItemModel(al *mediaprovider.Album) widgets.GridViewItemModel {
|
||||
return widgets.GridViewItemModel{
|
||||
Name: al.Name,
|
||||
ID: al.ID,
|
||||
CoverArtID: al.CoverArtID,
|
||||
Secondary: []string{strconv.Itoa(al.YearOrZero())},
|
||||
CanFavorite: true,
|
||||
IsFavorite: al.Favorite,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ArtistPage) getGroupedReleasesModel() widgets.GroupedReleasesModel {
|
||||
if a.artistInfo == nil {
|
||||
return widgets.GroupedReleasesModel{}
|
||||
}
|
||||
albums := []*mediaprovider.Album{}
|
||||
compilations := []*mediaprovider.Album{}
|
||||
eps := []*mediaprovider.Album{}
|
||||
singles := []*mediaprovider.Album{}
|
||||
|
||||
for _, album := range a.artistInfo.Albums {
|
||||
switch rt := album.ReleaseTypes; {
|
||||
case rt&mediaprovider.ReleaseTypeEP > 0:
|
||||
eps = append(eps, album)
|
||||
case rt&mediaprovider.ReleaseTypeCompilation > 0:
|
||||
compilations = append(compilations, album)
|
||||
case rt&mediaprovider.ReleaseTypeSingle > 0:
|
||||
singles = append(singles, album)
|
||||
default:
|
||||
albums = append(albums, album)
|
||||
}
|
||||
}
|
||||
|
||||
a.sortAlbumsSlices(albums, compilations, eps, singles)
|
||||
return widgets.GroupedReleasesModel{
|
||||
Albums: sharedutil.MapSlice(albums, a.albumToGridViewItemModel),
|
||||
Compilations: sharedutil.MapSlice(compilations, a.albumToGridViewItemModel),
|
||||
EPs: sharedutil.MapSlice(eps, a.albumToGridViewItemModel),
|
||||
Singles: sharedutil.MapSlice(singles, a.albumToGridViewItemModel),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ArtistPage) sortAlbumsSlices(slices ...[]*mediaprovider.Album) {
|
||||
var curSlice []*mediaprovider.Album
|
||||
sortFunc := func(x, y int) bool {
|
||||
return a.artistInfo.Albums[y].Date.After(a.artistInfo.Albums[x].Date)
|
||||
return curSlice[y].Date.After(curSlice[x].Date)
|
||||
}
|
||||
switch a.cfg.DiscographySort {
|
||||
case discographySorts[1]: /*year descending*/
|
||||
sortFunc = func(x, y int) bool {
|
||||
return a.artistInfo.Albums[x].Date.After(a.artistInfo.Albums[y].Date)
|
||||
return curSlice[x].Date.After(curSlice[y].Date)
|
||||
}
|
||||
case discographySorts[2]: /*name*/
|
||||
sortFunc = func(x, y int) bool {
|
||||
return a.artistInfo.Albums[x].Name < a.artistInfo.Albums[y].Name
|
||||
return curSlice[x].Name < curSlice[y].Name
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(a.artistInfo.Albums, sortFunc)
|
||||
return sharedutil.MapSlice(a.artistInfo.Albums, func(al *mediaprovider.Album) widgets.GridViewItemModel {
|
||||
return widgets.GridViewItemModel{
|
||||
Name: al.Name,
|
||||
ID: al.ID,
|
||||
CoverArtID: al.CoverArtID,
|
||||
Secondary: []string{strconv.Itoa(al.YearOrZero())},
|
||||
CanFavorite: true,
|
||||
IsFavorite: al.Favorite,
|
||||
}
|
||||
})
|
||||
for _, slice := range slices {
|
||||
curSlice = slice
|
||||
sort.Slice(slice, sortFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// should be called asynchronously
|
||||
@@ -254,27 +317,68 @@ func (a *ArtistPage) load() {
|
||||
}
|
||||
|
||||
func (a *ArtistPage) showAlbumGrid(reSort bool) {
|
||||
if a.albumGrid == nil {
|
||||
allAlbums := func() bool {
|
||||
return slices.IndexFunc(a.artistInfo.Albums, func(al *mediaprovider.Album) bool {
|
||||
return al.ReleaseTypes&mediaprovider.ReleaseTypeCompilation > 0 ||
|
||||
al.ReleaseTypes&mediaprovider.ReleaseTypeEP > 0 ||
|
||||
al.ReleaseTypes&mediaprovider.ReleaseTypeSingle > 0
|
||||
}) < 0
|
||||
}
|
||||
useGroupedReleases := a.artistInfo != nil && len(a.artistInfo.Albums) <= 50 && !allAlbums()
|
||||
|
||||
if a.albumGrid == nil && a.groupedReleases == nil {
|
||||
if a.artistInfo == nil {
|
||||
// page not loaded yet or invalid artist
|
||||
a.activeView = 0 // if page still loading, will show discography view first
|
||||
return
|
||||
}
|
||||
model := a.getGridViewAlbumsModel()
|
||||
if g := a.pool.Obtain(util.WidgetTypeGridView); g != nil {
|
||||
a.albumGrid = g.(*widgets.GridView)
|
||||
a.albumGrid.Placeholder = myTheme.AlbumIcon
|
||||
a.albumGrid.ResetFixed(model)
|
||||
if useGroupedReleases {
|
||||
model := a.getGroupedReleasesModel()
|
||||
if g := a.pool.Obtain(util.WidgetTypeGroupedReleases); g != nil {
|
||||
a.groupedReleases = g.(*widgets.GroupedReleases)
|
||||
a.groupedReleases.Model = model
|
||||
} else {
|
||||
a.groupedReleases = widgets.NewGroupedReleases(model, a.im)
|
||||
}
|
||||
a.contr.ConnectGroupedReleasesActions(a.groupedReleases)
|
||||
if a.sectionVisNeedApply {
|
||||
a.groupedReleases.SetSectionVisibility(a.sectionVis, true)
|
||||
a.sectionVisNeedApply = false
|
||||
}
|
||||
if a.gridScrollPos != 0 {
|
||||
a.groupedReleases.ScrollToOffset(a.gridScrollPos)
|
||||
a.gridScrollPos = 0
|
||||
}
|
||||
} else {
|
||||
a.albumGrid = widgets.NewFixedGridView(model, a.im, myTheme.AlbumIcon)
|
||||
model := a.getGridViewAlbumsModel()
|
||||
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)
|
||||
if a.gridScrollPos != 0 {
|
||||
a.albumGrid.ScrollToOffset(a.gridScrollPos)
|
||||
a.gridScrollPos = 0
|
||||
}
|
||||
}
|
||||
a.contr.ConnectAlbumGridActions(a.albumGrid)
|
||||
} else if reSort {
|
||||
model := a.getGridViewAlbumsModel()
|
||||
a.albumGrid.ResetFixed(model)
|
||||
if useGroupedReleases {
|
||||
a.groupedReleases.Model = a.getGroupedReleasesModel()
|
||||
} else {
|
||||
model := a.getGridViewAlbumsModel()
|
||||
a.albumGrid.ResetFixed(model)
|
||||
}
|
||||
}
|
||||
a.sortButton.Show()
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid
|
||||
if useGroupedReleases {
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.groupedReleases
|
||||
a.container.Objects[0].Refresh()
|
||||
} else {
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid
|
||||
}
|
||||
a.container.Objects[0].Refresh()
|
||||
}
|
||||
|
||||
@@ -323,6 +427,10 @@ func (a *ArtistPage) showTopTracks() {
|
||||
}
|
||||
tl.SetNowPlaying(a.nowPlayingID)
|
||||
a.contr.ConnectTracklistActions(tl)
|
||||
if a.listScrollPos != 0 {
|
||||
tl.ScrollToOffset(a.listScrollPos)
|
||||
a.listScrollPos = 0
|
||||
}
|
||||
a.tracklistCtr = container.New(
|
||||
&layout.CustomPaddedLayout{LeftPadding: 15, RightPadding: 15, BottomPadding: 10},
|
||||
tl)
|
||||
@@ -357,7 +465,7 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (s *artistPageState) Restore() Page {
|
||||
return newArtistPage(s.artistID, s.cfg, s.pool, s.pm, s.mp, s.im, s.contr, s.activeView, s.trackSort)
|
||||
return newArtistPage(*s)
|
||||
}
|
||||
|
||||
const artistBioNotAvailableKey = "Artist biography not available."
|
||||
|
||||
@@ -97,35 +97,67 @@ func (m *Controller) ConnectAlbumGridActions(grid *widgets.GridView) {
|
||||
grid.OnShowSecondaryPage = func(artistID string) {
|
||||
m.NavigateTo(ArtistRoute(artistID))
|
||||
}
|
||||
grid.OnAddToPlaylist = func(albumID string) {
|
||||
go func() {
|
||||
album, err := m.App.ServerManager.Server.GetAlbum(albumID)
|
||||
if err != nil {
|
||||
log.Printf("error loading album: %s", err.Error())
|
||||
return
|
||||
}
|
||||
fyne.Do(func() {
|
||||
m.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(album.Tracks))
|
||||
})
|
||||
}()
|
||||
}
|
||||
grid.OnDownload = func(albumID string) {
|
||||
go func() {
|
||||
album, err := m.App.ServerManager.Server.GetAlbum(albumID)
|
||||
if err != nil {
|
||||
log.Printf("error loading album: %s", err.Error())
|
||||
return
|
||||
}
|
||||
fyne.Do(func() {
|
||||
m.ShowDownloadDialog(album.Tracks, album.Name)
|
||||
})
|
||||
}()
|
||||
}
|
||||
grid.OnAddToPlaylist = m.onAddAlbumToPlaylist
|
||||
grid.OnDownload = m.onDownloadAlbum
|
||||
grid.OnShare = func(albumID string) {
|
||||
m.ShowShareDialog(albumID)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Controller) ConnectGroupedReleasesActions(grid *widgets.GroupedReleases) {
|
||||
grid.OnAddToQueue = func(albumID string) {
|
||||
go m.App.PlaybackManager.LoadAlbum(albumID, backend.Append, false)
|
||||
}
|
||||
grid.OnPlayNext = func(albumID string) {
|
||||
go m.App.PlaybackManager.LoadAlbum(albumID, backend.InsertNext, false)
|
||||
}
|
||||
grid.OnPlay = func(albumID string, shuffle bool) {
|
||||
go m.App.PlaybackManager.PlayAlbum(albumID, 0, shuffle)
|
||||
}
|
||||
grid.OnFavorite = func(albumID string, favorite bool) {
|
||||
m.App.ServerManager.Server.SetFavorite(mediaprovider.RatingFavoriteParameters{
|
||||
AlbumIDs: []string{albumID},
|
||||
}, favorite)
|
||||
}
|
||||
grid.OnShowItemPage = func(albumID string) {
|
||||
m.NavigateTo(AlbumRoute(albumID))
|
||||
}
|
||||
grid.OnShowSecondaryPage = func(artistID string) {
|
||||
m.NavigateTo(ArtistRoute(artistID))
|
||||
}
|
||||
grid.OnAddToPlaylist = m.onAddAlbumToPlaylist
|
||||
grid.OnDownload = m.onDownloadAlbum
|
||||
grid.OnShare = func(albumID string) {
|
||||
m.ShowShareDialog(albumID)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Controller) onAddAlbumToPlaylist(albumID string) {
|
||||
go func() {
|
||||
album, err := m.App.ServerManager.Server.GetAlbum(albumID)
|
||||
if err != nil {
|
||||
log.Printf("error loading album: %s", err.Error())
|
||||
return
|
||||
}
|
||||
fyne.Do(func() {
|
||||
m.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(album.Tracks))
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *Controller) onDownloadAlbum(albumID string) {
|
||||
go func() {
|
||||
album, err := m.App.ServerManager.Server.GetAlbum(albumID)
|
||||
if err != nil {
|
||||
log.Printf("error loading album: %s", err.Error())
|
||||
return
|
||||
}
|
||||
fyne.Do(func() {
|
||||
m.ShowDownloadDialog(album.Tracks, album.Name)
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func (m *Controller) ConnectArtistGridActions(grid *widgets.GridView) {
|
||||
grid.OnShowItemPage = func(id string) { m.NavigateTo(ArtistRoute(id)) }
|
||||
grid.OnPlayNext = func(artistID string) {
|
||||
|
||||
@@ -17,6 +17,7 @@ const (
|
||||
WidgetTypeTracklist
|
||||
WidgetTypeCompactTracklist
|
||||
WidgetTypeNowPlayingPage
|
||||
WidgetTypeGroupedReleases
|
||||
|
||||
// keep at bottom
|
||||
numWidgetTypes
|
||||
|
||||
@@ -362,7 +362,7 @@ func (g *GridView) doUpdateItemCard(itemIdx int, card *GridViewItem) {
|
||||
|
||||
card.ShowSuffix = g.ShowSuffix
|
||||
card.Cover.Im.PlaceholderIcon = g.Placeholder
|
||||
if !card.NeedsUpdate(item) {
|
||||
if !card.NeedsUpdate(&item) {
|
||||
// nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ func NewGridViewItem(placeholderResource fyne.Resource) *GridViewItem {
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GridViewItem) NeedsUpdate(model GridViewItemModel) bool {
|
||||
func (g *GridViewItem) NeedsUpdate(model *GridViewItemModel) bool {
|
||||
return g.itemID != model.ID || !slices.Equal(g.secondaryIDs, model.SecondaryIDs) ||
|
||||
(g.ShowSuffix && g.secondaryText.Suffix != model.Suffix) ||
|
||||
(!g.ShowSuffix && g.secondaryText.Suffix != "")
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/dweymouth/supersonic/backend"
|
||||
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||
"github.com/dweymouth/supersonic/ui/util"
|
||||
)
|
||||
|
||||
type GroupedReleasesModel struct {
|
||||
Albums []GridViewItemModel
|
||||
Compilations []GridViewItemModel
|
||||
EPs []GridViewItemModel
|
||||
Singles []GridViewItemModel
|
||||
}
|
||||
|
||||
type GroupedReleasesSectionVisibility struct {
|
||||
Albums bool
|
||||
Compilations bool
|
||||
EPs bool
|
||||
Singles bool
|
||||
}
|
||||
|
||||
type GroupedReleases struct {
|
||||
widget.BaseWidget
|
||||
|
||||
ShowSuffix bool
|
||||
|
||||
Model GroupedReleasesModel
|
||||
|
||||
OnPlay func(id string, shuffle bool)
|
||||
OnPlayNext func(id string)
|
||||
OnAddToQueue func(id string)
|
||||
OnAddToPlaylist func(id string)
|
||||
OnFavorite func(id string, fav bool)
|
||||
OnDownload func(id string)
|
||||
OnShare func(id string)
|
||||
OnShowItemPage func(id string)
|
||||
OnShowSecondaryPage func(id string)
|
||||
|
||||
menu *widget.PopUpMenu
|
||||
shareMenuItem *fyne.MenuItem
|
||||
menuGridViewItemId string
|
||||
imageFetcher util.ImageFetcher
|
||||
cardPool sync.Pool
|
||||
|
||||
sections [4]groupedReleasesSection
|
||||
|
||||
container *container.Scroll
|
||||
}
|
||||
|
||||
type groupedReleasesSection struct {
|
||||
titleRow *groupedReleasesHeader
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
func NewGroupedReleases(model GroupedReleasesModel, fetch util.ImageFetcher) *GroupedReleases {
|
||||
g := &GroupedReleases{
|
||||
Model: model,
|
||||
imageFetcher: fetch,
|
||||
}
|
||||
g.cardPool.New = func() any { return g.createNewItemCard() }
|
||||
g.ExtendBaseWidget(g)
|
||||
|
||||
cardSize := fyne.NewSquareSize(backend.AppInstance().Config.GridView.CardSize)
|
||||
sections := []string{lang.L("Albums"), lang.L("Compilations"), lang.L("EPs"), lang.L("Singles")}
|
||||
for i, s := range sections {
|
||||
_i := i
|
||||
g.sections[i].titleRow = newGroupedReleasesHeader(s, func(collapse bool) {
|
||||
if collapse {
|
||||
g.sections[_i].container.Hide()
|
||||
} else {
|
||||
g.sections[_i].container.Show()
|
||||
}
|
||||
canvas.Refresh(g)
|
||||
})
|
||||
g.sections[i].container = container.NewGridWrap(cardSize)
|
||||
}
|
||||
|
||||
vbox := container.NewVBox()
|
||||
for i := range g.sections {
|
||||
vbox.Add(g.sections[i].titleRow)
|
||||
vbox.Add(g.sections[i].container)
|
||||
}
|
||||
g.container = container.NewVScroll(vbox)
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) GetSectionVisibility() GroupedReleasesSectionVisibility {
|
||||
v := GroupedReleasesSectionVisibility{true, true, true, true}
|
||||
if g.sections[0].titleRow.Collapsed {
|
||||
v.Albums = false
|
||||
}
|
||||
if g.sections[1].titleRow.Collapsed {
|
||||
v.Compilations = false
|
||||
}
|
||||
if g.sections[2].titleRow.Collapsed {
|
||||
v.EPs = false
|
||||
}
|
||||
if g.sections[3].titleRow.Collapsed {
|
||||
v.Singles = false
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) SetSectionVisibility(v GroupedReleasesSectionVisibility, refresh bool) {
|
||||
for i, b := range []bool{v.Albums, v.Compilations, v.EPs, v.Singles} {
|
||||
g.sections[i].titleRow.Collapsed = !b
|
||||
g.sections[i].container.Hidden = !b
|
||||
}
|
||||
if refresh {
|
||||
g.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(g.container)
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) GetScrollOffset() float32 {
|
||||
return g.container.Offset.Y
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) ScrollToOffset(offs float32) {
|
||||
g.container.ScrollToOffset(fyne.NewPos(0, offs))
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) Refresh() {
|
||||
gridSize := backend.AppInstance().Config.GridView.CardSize
|
||||
sectionItems := [4][]GridViewItemModel{g.Model.Albums, g.Model.Compilations, g.Model.EPs, g.Model.Singles}
|
||||
layoutSize := fyne.NewSize(0, 0)
|
||||
for i, items := range sectionItems {
|
||||
lenItems := len(items)
|
||||
objects := g.sections[i].container.Objects
|
||||
// clear out excess cards in this section
|
||||
for x := lenItems; x < len(objects); x++ {
|
||||
g.cardPool.Put(objects[x])
|
||||
objects[x] = nil
|
||||
}
|
||||
if len(objects) > lenItems {
|
||||
objects = objects[:lenItems]
|
||||
}
|
||||
|
||||
// update existing cards
|
||||
for x := 0; x < len(objects); x++ {
|
||||
g.doUpdateItemCard(objects[x].(*GridViewItem), &items[x])
|
||||
objects[x].(*GridViewItem).SetSize(gridSize)
|
||||
if layoutSize.IsZero() {
|
||||
layoutSize = objects[x].MinSize()
|
||||
}
|
||||
}
|
||||
// append new ones as needed
|
||||
for x := len(objects); x < lenItems; x++ {
|
||||
card := g.cardPool.Get().(*GridViewItem)
|
||||
card.SetSize(gridSize)
|
||||
g.doUpdateItemCard(card, &items[x])
|
||||
objects = append(objects, card)
|
||||
if layoutSize.IsZero() {
|
||||
layoutSize = card.MinSize()
|
||||
}
|
||||
}
|
||||
|
||||
l := layout.NewGridWrapLayout(layoutSize)
|
||||
g.sections[i].container.Layout = l
|
||||
// needed to initialize the col count so that MinSize is correct
|
||||
l.Layout(objects, g.Size())
|
||||
g.sections[i].container.Objects = objects
|
||||
// if section has no albums in it, hide
|
||||
if lenItems == 0 {
|
||||
g.sections[i].titleRow.Hide()
|
||||
g.sections[i].container.Hide()
|
||||
} else {
|
||||
g.sections[i].titleRow.Show()
|
||||
if !g.sections[i].titleRow.Collapsed {
|
||||
g.sections[i].container.Show()
|
||||
}
|
||||
}
|
||||
}
|
||||
g.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) createNewItemCard() fyne.CanvasObject {
|
||||
card := NewGridViewItem(myTheme.AlbumIcon)
|
||||
card.SetSize(backend.AppInstance().Config.GridView.CardSize)
|
||||
card.ItemIndex = -1
|
||||
card.ImgLoader = util.NewThumbnailLoader(g.imageFetcher, card.Cover.SetImage)
|
||||
card.ImgLoader.OnBeforeLoad = func() { card.Cover.SetImage(nil) }
|
||||
card.OnPlay = func() { g.onPlay(card.ItemID(), false) }
|
||||
card.OnFavorite = func(fav bool) {
|
||||
if g.OnFavorite != nil {
|
||||
g.OnFavorite(card.itemID, fav)
|
||||
}
|
||||
}
|
||||
card.OnShowSecondaryPage = func(id string) {
|
||||
if g.OnShowSecondaryPage != nil {
|
||||
g.OnShowSecondaryPage(id)
|
||||
}
|
||||
}
|
||||
card.OnShowItemPage = func() {
|
||||
if g.OnShowItemPage != nil {
|
||||
g.OnShowItemPage(card.ItemID())
|
||||
}
|
||||
}
|
||||
card.OnShowContextMenu = func(p fyne.Position) {
|
||||
g.showContextMenu(card, p)
|
||||
}
|
||||
return card
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) showContextMenu(card *GridViewItem, pos fyne.Position) {
|
||||
g.menuGridViewItemId = card.ItemID()
|
||||
if g.menu == nil {
|
||||
play := fyne.NewMenuItem(lang.L("Play"), func() { g.onPlay(g.menuGridViewItemId, false) })
|
||||
play.Icon = theme.MediaPlayIcon()
|
||||
shuffle := fyne.NewMenuItem(lang.L("Shuffle"), func() { g.onPlay(g.menuGridViewItemId, true) })
|
||||
shuffle.Icon = myTheme.ShuffleIcon
|
||||
queueNext := fyne.NewMenuItem(lang.L("Play next"), func() {
|
||||
if g.OnPlayNext != nil {
|
||||
g.OnPlayNext(g.menuGridViewItemId)
|
||||
}
|
||||
})
|
||||
queueNext.Icon = myTheme.PlayNextIcon
|
||||
queue := fyne.NewMenuItem(lang.L("Add to queue"), func() {
|
||||
if g.OnAddToQueue != nil {
|
||||
g.OnAddToQueue(g.menuGridViewItemId)
|
||||
}
|
||||
})
|
||||
queue.Icon = theme.ContentAddIcon()
|
||||
playlist := fyne.NewMenuItem(lang.L("Add to playlist")+"...", func() {
|
||||
if g.OnAddToPlaylist != nil {
|
||||
g.OnAddToPlaylist(g.menuGridViewItemId)
|
||||
}
|
||||
})
|
||||
playlist.Icon = myTheme.PlaylistIcon
|
||||
download := fyne.NewMenuItem(lang.L("Download")+"...", func() {
|
||||
if g.OnDownload != nil {
|
||||
g.OnDownload(g.menuGridViewItemId)
|
||||
}
|
||||
})
|
||||
download.Icon = theme.DownloadIcon()
|
||||
g.shareMenuItem = fyne.NewMenuItem(lang.L("Share")+"...", func() {
|
||||
g.OnShare(g.menuGridViewItemId)
|
||||
})
|
||||
g.shareMenuItem.Icon = myTheme.ShareIcon
|
||||
g.menu = widget.NewPopUpMenu(fyne.NewMenu("", play, shuffle, queueNext, queue, playlist, download, g.shareMenuItem),
|
||||
fyne.CurrentApp().Driver().CanvasForObject(g))
|
||||
}
|
||||
g.menu.ShowAtPosition(pos)
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) onPlay(itemID string, shuffle bool) {
|
||||
if g.OnPlay != nil {
|
||||
g.OnPlay(itemID, shuffle)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupedReleases) doUpdateItemCard(card *GridViewItem, model *GridViewItemModel) {
|
||||
card.ShowSuffix = g.ShowSuffix
|
||||
if !card.NeedsUpdate(model) {
|
||||
// nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
card.Update(model)
|
||||
card.ImgLoader.Load(model.CoverArtID)
|
||||
}
|
||||
|
||||
type groupedReleasesHeader struct {
|
||||
widget.BaseWidget
|
||||
|
||||
title string
|
||||
onToggle func(bool)
|
||||
|
||||
icon *widget.Icon
|
||||
Collapsed bool
|
||||
}
|
||||
|
||||
func newGroupedReleasesHeader(title string, onToggleVisibility func(bool)) *groupedReleasesHeader {
|
||||
g := &groupedReleasesHeader{
|
||||
title: title,
|
||||
icon: widget.NewIcon(theme.MenuDropDownIcon()),
|
||||
onToggle: onToggleVisibility,
|
||||
}
|
||||
g.ExtendBaseWidget(g)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *groupedReleasesHeader) CreateRenderer() fyne.WidgetRenderer {
|
||||
titleText := widget.NewLabelWithStyle(g.title, fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||
titleText.SizeName = theme.SizeNameSubHeadingText
|
||||
return widget.NewSimpleRenderer(
|
||||
container.NewHBox(util.NewHSpace(8), titleText, util.NewHSpace(-theme.Padding()*2), g.icon),
|
||||
)
|
||||
}
|
||||
|
||||
func (g *groupedReleasesHeader) Refresh() {
|
||||
if g.Collapsed {
|
||||
g.icon.Resource = theme.MenuExpandIcon()
|
||||
} else {
|
||||
g.icon.Resource = theme.MenuDropDownIcon()
|
||||
}
|
||||
g.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.Tappable = (*groupedReleasesHeader)(nil)
|
||||
|
||||
func (g *groupedReleasesHeader) Tapped(*fyne.PointEvent) {
|
||||
g.Collapsed = !g.Collapsed
|
||||
g.onToggle(g.Collapsed)
|
||||
g.Refresh()
|
||||
}
|
||||
Reference in New Issue
Block a user