diff --git a/emmental/sidebar/section.py b/emmental/sidebar/section.py index c2f10e3..6dee2b7 100644 --- a/emmental/sidebar/section.py +++ b/emmental/sidebar/section.py @@ -97,3 +97,54 @@ class Section(header.Header): @GObject.Signal(arg_types=(db.playlist.Playlist,)) def playlist_selected(self, playlist: db.playlist.Playlist): """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) diff --git a/tests/sidebar/test_section.py b/tests/sidebar/test_section.py index d46776a..2317816 100644 --- a/tests/sidebar/test_section.py +++ b/tests/sidebar/test_section.py @@ -4,6 +4,7 @@ import emmental.db import emmental.sidebar.section import tests.util import unittest.mock +from gi.repository import GObject from gi.repository import GLib from gi.repository import Gtk @@ -141,3 +142,94 @@ class TestSection(tests.util.TestCase): playlist = self.table.create("Test Playlist") self.section._listview.emit("activate", 0) 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())