sidebar: Create a Section Group

This group manages a list of sections so only one is active at a time.
Additionally, it adjusts the animation property of each section to match
the direction the header moves when activated.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-08-30 10:58:46 -04:00
parent b316184bf1
commit eb0b005c75
2 changed files with 143 additions and 0 deletions

View File

@ -97,3 +97,54 @@ class Section(header.Header):
@GObject.Signal(arg_types=(db.playlist.Playlist,)) @GObject.Signal(arg_types=(db.playlist.Playlist,))
def playlist_selected(self, playlist: db.playlist.Playlist): def playlist_selected(self, playlist: db.playlist.Playlist):
"""Signal that the selected playlist has changed.""" """Signal that the selected playlist has changed."""
class Group(GObject.GObject):
"""A group of sections."""
sql = GObject.Property(type=db.Connection)
current = GObject.Property(type=Section)
selected_section = GObject.Property(type=Section)
selected_playlist = GObject.Property(type=db.playlist.Playlist)
def __init__(self, sql: db.Connection):
"""Initialize a Section Group."""
super().__init__(sql=sql)
self._sections = []
def __on_active(self, section: Section, param: GObject.ParamSpec) -> None:
if section.active:
new = self._sections.index(section)
prev = len(self._sections)
if self.current and self.current is not section:
prev = self._sections.index(self.current)
self.current.active = False
self.current = section
if new < prev:
section.animation = Gtk.RevealerTransitionType.SLIDE_DOWN
else:
section.animation = Gtk.RevealerTransitionType.SLIDE_UP
if section is self.selected_section:
section.select_playlist(self.selected_playlist)
def __playlist_activated(self, section: Section,
playlist: db.playlist.Playlist) -> None:
self.sql.set_active_playlist(playlist)
def __playlist_selected(self, section: Section,
playlist: db.playlist.Playlist) -> None:
if self.selected_section and self.selected_section is not section:
self.selected_section.clear_selection()
self.selected_section = section
self.selected_playlist = playlist
def add(self, section: Section) -> None:
"""Add a section to the group."""
self._sections.append(section)
section.connect("notify::active", self.__on_active)
section.connect("playlist-activated", self.__playlist_activated)
section.connect("playlist-selected", self.__playlist_selected)

View File

@ -4,6 +4,7 @@ import emmental.db
import emmental.sidebar.section import emmental.sidebar.section
import tests.util import tests.util
import unittest.mock import unittest.mock
from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
from gi.repository import Gtk from gi.repository import Gtk
@ -141,3 +142,94 @@ class TestSection(tests.util.TestCase):
playlist = self.table.create("Test Playlist") playlist = self.table.create("Test Playlist")
self.section._listview.emit("activate", 0) self.section._listview.emit("activate", 0)
playlist_activated.assert_called_with(self.section, playlist) playlist_activated.assert_called_with(self.section, playlist)
class TestGroup(tests.util.TestCase):
"""Test our sidebar section group."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.group = emmental.sidebar.section.Group(self.sql)
self.row_type = emmental.sidebar.row.TreeRow
self.section1 = emmental.sidebar.section.Section(self.sql.playlists,
self.row_type)
self.section2 = emmental.sidebar.section.Section(self.sql.genres,
self.row_type)
self.section1.do_get_subtitle = unittest.mock.Mock(return_value="")
self.section2.do_get_subtitle = unittest.mock.Mock(return_value="")
def test_init(self):
"""Test that the Group is set up properly."""
self.assertIsInstance(self.group, GObject.GObject)
self.assertListEqual(self.group._sections, [])
self.assertEqual(self.group.sql, self.sql)
def test_add(self):
"""Test adding sections to the Group."""
self.group.add(self.section1)
self.assertListEqual(self.group._sections, [self.section1])
self.group.add(self.section2)
self.assertListEqual(self.group._sections,
[self.section1, self.section2])
def test_current(self):
"""Test the current section property."""
self.group.add(self.section1)
self.group.add(self.section2)
self.assertIsNone(self.group.current)
self.section1.active = True
self.assertEqual(self.group.current, self.section1)
self.section2.active = True
self.assertEqual(self.group.current, self.section2)
self.assertFalse(self.section1.active)
def test_animation(self):
"""Test setting the section animation style."""
self.group.add(self.section1)
self.group.add(self.section2)
self.section1.active = True
self.assertEqual(self.section1.animation,
Gtk.RevealerTransitionType.SLIDE_DOWN)
self.section2.active = True
self.assertEqual(self.section2.animation,
Gtk.RevealerTransitionType.SLIDE_UP)
def test_playlist_activated(self):
"""Test responding to the section playlist-activated signal."""
self.group.add(self.section1)
self.group.add(self.section2)
self.assertIsNone(self.sql.active_playlist)
playlist = self.sql.playlists.create("Test Playlist")
self.section1.emit("playlist-activated", playlist)
self.assertEqual(self.sql.active_playlist, playlist)
genre = self.sql.genres.create("Test Genre")
self.section2.emit("playlist-activated", genre)
self.assertEqual(self.sql.active_playlist, genre)
def test_selections(self):
"""Test the selected section & playlist properties."""
self.group.add(self.section1)
self.group.add(self.section2)
self.assertIsNone(self.group.selected_section)
self.assertIsNone(self.group.selected_playlist)
genre = self.sql.genres.create("Test Genre")
self.section2.emit("playlist-selected", genre)
self.assertEqual(self.group.selected_section, self.section2)
self.assertEqual(self.group.selected_playlist, genre)
self.section2.active = True
treerow = self.section2._selection.get_selected_item()
self.assertEqual(treerow.get_item(), genre)
playlist = self.sql.playlists.create("Test Playlist")
self.section1.emit("playlist-selected", playlist)
self.assertIsNone(self.section2._selection.get_selected_item())