sidebar: Create a Section for Artists
This section shows a tree of Artist and Album playlists. I use the library-artists icon from the gnome icon-library as the section header, and the music-artist / music-artist2 icons for the "show all" button to indicate state.. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
711c04cb29
commit
2f239bd94d
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""Displays our artist and album tree."""
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from ..buttons import ImageToggle
|
||||
from .. import db
|
||||
from . import row
|
||||
from . import section
|
||||
|
||||
|
||||
class ArtistRow(row.TreeRow):
|
||||
"""A factory for setting Album covers properly."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize an ArtistRow."""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.child = row.Row()
|
||||
|
||||
def do_bind(self) -> None:
|
||||
"""Bind the album cover property."""
|
||||
super().do_bind()
|
||||
if isinstance(self.item, db.albums.Album):
|
||||
self.bind_and_set_property("cover", "image")
|
||||
|
||||
|
||||
class Section(section.Section):
|
||||
"""A sidebar Section for the artist and album playlist tree."""
|
||||
|
||||
album_table = GObject.Property(type=db.albums.Table)
|
||||
show_all = GObject.Property(type=bool, default=False)
|
||||
|
||||
def __init__(self, artist_table: db.artists.Table,
|
||||
album_table: db.albums.Table):
|
||||
"""Initialize our artist & album section."""
|
||||
super().__init__(artist_table, ArtistRow, title="Artists & Albums",
|
||||
subtitle="0 artists, 0 albums",
|
||||
icon_name="library-artists", album_table=album_table)
|
||||
self.extra_widget = ImageToggle("music-artist", "music-artist2",
|
||||
icon_size=Gtk.IconSize.NORMAL,
|
||||
has_frame=False)
|
||||
self.album_table.connect("items-changed", self.__update_subtitle)
|
||||
self.bind_property("show-all", self.extra_widget, "active",
|
||||
GObject.BindingFlags.BIDIRECTIONAL)
|
||||
self.bind_property("show-all", artist_table, "show-all")
|
||||
|
||||
def __update_subtitle(self, table: db.albums.Table, position: int,
|
||||
removed: int, added: int) -> None:
|
||||
self.subtitle = self.do_get_subtitle(0)
|
||||
|
||||
def do_get_subtitle(self, n_items: int) -> str:
|
||||
"""Return a subtitle for this section."""
|
||||
n_artists = len(self.table)
|
||||
s_artists = "s" if n_artists != 1 else ""
|
||||
n_albums = len(self.album_table)
|
||||
s_albums = "s" if n_albums != 1 else ""
|
||||
return f"{n_artists} artist{s_artists}, {n_albums} album{s_albums}"
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 5 0 c -1.089844 0 -2 0.910156 -2 2 v 9 c 0 1.089844 0.910156 2 2 2 h 9 c 1.089844 0 2 -0.910156 2 -2 v -9 c 0 -1.089844 -0.910156 -2 -2 -2 z m 4.5 2 c 1.105469 0 2 0.894531 2 2 s -0.894531 2 -2 2 s -2 -0.894531 -2 -2 s 0.894531 -2 2 -2 z m -0.5 5 h 1 c 1.660156 0 3 1.339844 3 3 v 0.332031 c 0 0.667969 -0.667969 0.667969 -0.667969 0.667969 h -5.664062 s -0.667969 0 -0.667969 -0.667969 v -0.332031 c 0 -1.660156 1.339844 -3 3 -3 z m 0 0"/><path d="m 2 3 c -1.089844 0 -2 0.910156 -2 2 v 9 c 0 1.089844 0.910156 2 2 2 h 9 c 1.089844 0 2 -0.910156 2 -2 h -11 z m 0 0"/></g></svg>
|
After Width: | Height: | Size: 734 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 8 2 c -1.65625 0 -3 1.34375 -3 3 s 1.34375 3 3 3 s 3 -1.34375 3 -3 s -1.34375 -3 -3 -3 z m -1.5 7 c -2.492188 0 -4.5 2.007812 -4.5 4.5 v 0.5 c 0 1 1 1 1 1 h 6.1875 c -0.113281 -0.316406 -0.1875 -0.648438 -0.1875 -1 c 0 -1.644531 1.355469 -3 3 -3 v -1.242188 c -0.714844 -0.480468 -1.574219 -0.757812 -2.5 -0.757812 z m 5.5 4 c -0.566406 0 -1 0.4375 -1 1 s 0.4375 1 1 1 s 1 -0.4375 1 -1 v -0.574219 l -0.5 -0.289062 c -0.152344 -0.089844 -0.328125 -0.136719 -0.5 -0.136719 z m 0 0"/><path d="m 13 9 v 3.269531 c -0.304688 -0.175781 -0.648438 -0.269531 -1 -0.269531 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 v -3 h 2 v -2 z m 0 0"/></g></svg>
|
After Width: | Height: | Size: 821 B |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222"><path d="m 8 2 c -1.65625 0 -3 1.34375 -3 3 s 1.34375 3 3 3 s 3 -1.34375 3 -3 s -1.34375 -3 -3 -3 z m 0 2 c 0.550781 0 1 0.449219 1 1 s -0.449219 1 -1 1 s -1 -0.449219 -1 -1 s 0.449219 -1 1 -1 z m 0 0"/><path d="m 13 9 v 3.269531 c -0.304688 -0.175781 -0.648438 -0.269531 -1 -0.269531 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 v -3 h 2 v -2 z m 0 0"/><path d="m 6 9 s -0.957031 -0.015625 -1.949219 0.480469 c -0.988281 0.496093 -2.050781 1.6875 -2.050781 3.519531 v 1 c -0.003906 0.359375 0.183594 0.695312 0.496094 0.878906 c 0.3125 0.179688 0.695312 0.179688 1.007812 0 c 0.3125 -0.183594 0.5 -0.519531 0.496094 -0.878906 v -1 c 0 -1.167969 0.4375 -1.476562 0.949219 -1.730469 c 0.507812 -0.253906 1.050781 -0.269531 1.050781 -0.269531 h 5 c 0.359375 0.003906 0.695312 -0.183594 0.878906 -0.496094 c 0.179688 -0.3125 0.179688 -0.695312 0 -1.007812 c -0.183594 -0.3125 -0.519531 -0.5 -0.878906 -0.496094 z m 0 0"/></g></svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,89 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""Tests our artist / album / disc section and tree."""
|
||||
import emmental.sidebar.artist
|
||||
import emmental.sidebar.row
|
||||
import tests.util
|
||||
import unittest.mock
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class TestArtist(tests.util.TestCase):
|
||||
"""Test our Artist section."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
super().setUp()
|
||||
self.artists = emmental.sidebar.artist.Section(self.sql.artists,
|
||||
self.sql.albums)
|
||||
|
||||
def test_init(self):
|
||||
"""Test that the artists section is set up correctly."""
|
||||
self.assertIsInstance(self.artists, emmental.sidebar.section.Section)
|
||||
self.assertEqual(self.artists._factory.row_type,
|
||||
emmental.sidebar.artist.ArtistRow)
|
||||
|
||||
self.assertEqual(self.artists.table, self.sql.artists)
|
||||
self.assertEqual(self.artists.album_table, self.sql.albums)
|
||||
self.assertEqual(self.artists.icon_name, "library-artists")
|
||||
self.assertEqual(self.artists.title, "Artists & Albums")
|
||||
|
||||
def test_extra_widget(self):
|
||||
"""Test the artist section extra widget."""
|
||||
self.assertIsInstance(self.artists.extra_widget,
|
||||
emmental.buttons.ImageToggle)
|
||||
self.assertEqual(self.artists.extra_widget.active_icon_name,
|
||||
"music-artist")
|
||||
self.assertEqual(self.artists.extra_widget.inactive_icon_name,
|
||||
"music-artist2")
|
||||
self.assertEqual(self.artists.extra_widget.icon_size,
|
||||
Gtk.IconSize.NORMAL)
|
||||
self.assertFalse(self.artists.extra_widget.get_has_frame())
|
||||
|
||||
def test_subtitle(self):
|
||||
"""Test that the subtitle property is set properly."""
|
||||
self.artists.show_all = True
|
||||
self.assertEqual(self.artists.subtitle, "0 artists, 0 albums")
|
||||
artist = self.sql.artists.create("Artist 1")
|
||||
self.assertEqual(self.artists.subtitle, "1 artist, 0 albums")
|
||||
artist.add_album(self.sql.albums.create("Album 1", "Artist 1", "2022"))
|
||||
self.assertEqual(self.artists.subtitle, "1 artist, 1 album")
|
||||
artist.add_album(self.sql.albums.create("Album 2", "Artist 1", "2023"))
|
||||
self.assertEqual(self.artists.subtitle, "1 artist, 2 albums")
|
||||
self.sql.artists.create("Artist 2")
|
||||
self.assertEqual(self.artists.subtitle, "2 artists, 2 albums")
|
||||
|
||||
def test_show_all(self):
|
||||
"""Test the show all property."""
|
||||
self.assertFalse(self.artists.show_all)
|
||||
self.artists.show_all = True
|
||||
self.assertTrue(self.artists.extra_widget.active)
|
||||
self.assertTrue(self.sql.artists.show_all)
|
||||
self.artists.extra_widget.active = False
|
||||
self.assertFalse(self.artists.show_all)
|
||||
self.assertFalse(self.sql.artists.show_all)
|
||||
|
||||
def test_artist_row(self):
|
||||
"""Test setting an album cover to the row icon."""
|
||||
artist = self.sql.artists.create("Test Artist")
|
||||
album = self.sql.albums.create("Test Album", "Test Artist", "2022")
|
||||
artist.add_album(album)
|
||||
|
||||
treeitem = Gtk.TreeListRow()
|
||||
treeitem.get_item = unittest.mock.Mock(return_value=artist)
|
||||
listitem = Gtk.ListItem()
|
||||
listitem.get_item = unittest.mock.Mock(return_value=treeitem)
|
||||
row = emmental.sidebar.artist.ArtistRow(listitem)
|
||||
self.assertIsInstance(row.child, emmental.sidebar.row.Row)
|
||||
self.assertTrue(row.indented)
|
||||
|
||||
row.bind()
|
||||
self.assertEqual(row.child.name, "Test Artist")
|
||||
row.unbind()
|
||||
|
||||
treeitem.get_item.return_value = album
|
||||
row.bind()
|
||||
self.assertEqual(row.child.name, "Test Album")
|
||||
self.assertIsNone(row.child.image)
|
||||
|
||||
album.cover = tests.util.COVER_JPG
|
||||
self.assertEqual(row.child.image, tests.util.COVER_JPG)
|
Loading…
Reference in New Issue