diff --git a/emmental/header/__init__.py b/emmental/header/__init__.py index 4b1d99b..2047e50 100644 --- a/emmental/header/__init__.py +++ b/emmental/header/__init__.py @@ -44,7 +44,7 @@ class Header(Gtk.HeaderBar): self._title = Adw.WindowTitle(title=self.title, subtitle=self.subtitle, tooltip_text=gsetup.env_string()) self._volume = volume.VolumeRow() - self._replaygain = replaygain.Selector() + self._replaygain = replaygain.ReplayGainRow() self._box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) self._box.append(self._volume) diff --git a/emmental/header/replaygain.py b/emmental/header/replaygain.py index 08e65ea..6f846fb 100644 --- a/emmental/header/replaygain.py +++ b/emmental/header/replaygain.py @@ -2,9 +2,36 @@ """A widget for selecting ReplayGain mode.""" from gi.repository import GObject from gi.repository import Gtk +from gi.repository import Adw -class Selector(Gtk.Grid): +class CheckRow(Adw.ActionRow): + """A custom Adw.ActionRow displaying a Check Button.""" + + active = GObject.Property(type=bool, default=False) + group = GObject.Property(type=Adw.ActionRow) + mode = GObject.Property(type=str) + + def __init__(self, mode: str, active: bool = False, + group: Adw.ActionRow | None = None, **kwargs): + """Initialize the Check Row.""" + super().__init__(mode=mode, active=active, group=group, **kwargs) + self._prefix = Gtk.CheckButton(active=active, + group=group._prefix if group else None) + + self.bind_property("active", self._prefix, "active", + GObject.BindingFlags.BIDIRECTIONAL) + + self.set_activatable_widget(self._prefix) + self.add_prefix(self._prefix) + + def set_active(self, newval: bool) -> None: + """Set the active property.""" + if self.active != newval: + self.active = newval + + +class ReplayGainRow(Adw.ExpanderRow): """Build up a widget for configuring ReplayGain settings.""" enabled = GObject.Property(type=bool, default=False) @@ -12,42 +39,40 @@ class Selector(Gtk.Grid): def __init__(self): """Initialize the ReplayGain selector.""" - super().__init__(column_spacing=6, margin_top=8) - self._title = Gtk.Label(label="Volume Normalization", yalign=0.8, - hexpand=True, vexpand=True) - self._switch = Gtk.Switch() - self._auto = Gtk.CheckButton(label="Decide automatically", - sensitive=False, active=True) - self._album = Gtk.CheckButton(label="Albums have the same volume", - sensitive=False, group=self._auto) - self._track = Gtk.CheckButton(label="Tracks have the same volume", - sensitive=False, group=self._auto) + super().__init__(title="Volume Normalization", + subtitle="Configure ReplayGain normalizing") + self._switch = Gtk.Switch(valign=Gtk.Align.CENTER) + self._automatic = CheckRow(title="Automatic Mode", + subtitle="Emmental decides automatically", + mode="auto", active=True) + self._album = CheckRow(title="Album Mode", + subtitle="Albums have the same volume", + mode="album", group=self._automatic) + self._track = CheckRow(title="Track Mode", + subtitle="Tracks have the same volume", + mode="track", group=self._automatic) - self.attach(self._title, 0, 0, 1, 1) - self.attach(self._switch, 1, 0, 1, 1) - self.attach(self._auto, 0, 1, 2, 1) - self.attach(self._album, 0, 2, 2, 1) - self.attach(self._track, 0, 3, 2, 1) + self.add_prefix(self._switch) + self.add_row(self._automatic) + self.add_row(self._album) + self.add_row(self._track) self.connect("notify::mode", self.__notify_mode) - self._auto.connect("toggled", self.__mode_toggled, "auto") - self._album.connect("toggled", self.__mode_toggled, "album") - self._track.connect("toggled", self.__mode_toggled, "track") + self._automatic.connect("notify::active", self.__row_activated) + self._album.connect("notify::active", self.__row_activated) + self._track.connect("notify::active", self.__row_activated) - self._switch.bind_property("state", self._auto, "sensitive") - self._switch.bind_property("state", self._album, "sensitive") - self._switch.bind_property("state", self._track, "sensitive") - self.bind_property("enabled", self._switch, "state", + self._switch.bind_property("active", self, "expanded", + GObject.BindingFlags.BIDIRECTIONAL) + self.bind_property("enabled", self._switch, "active", GObject.BindingFlags.BIDIRECTIONAL) - self._title.add_css_class("title-4") - - def __notify_mode(self, selector: Gtk.Grid, param) -> None: - match selector.get_property("mode"): + def __notify_mode(self, row: Adw.ExpanderRow, param) -> None: + match self.mode: case "album": self._album.set_active(True) case "track": self._track.set_active(True) - case _: self._auto.set_active(True) + case _: self._automatic.set_active(True) - def __mode_toggled(self, check: Gtk.CheckButton, new_mode: str) -> None: - if check.get_active(): - self.mode = new_mode + def __row_activated(self, row: CheckRow, param: GObject.ParamSpec) -> None: + if row.active: + self.mode = row.mode diff --git a/tests/header/test_header.py b/tests/header/test_header.py index f6e5b69..713916e 100644 --- a/tests/header/test_header.py +++ b/tests/header/test_header.py @@ -88,7 +88,7 @@ class TestHeader(tests.util.TestCase): def test_replaygain(self): """Test that we can configure ReplayGain as expected.""" self.assertIsInstance(self.header._replaygain, - emmental.header.replaygain.Selector) + emmental.header.replaygain.ReplayGainRow) self.assertFalse(self.header.rg_enabled) self.assertEqual(self.header.rg_mode, "auto") diff --git a/tests/header/test_replaygain.py b/tests/header/test_replaygain.py index 3ea9ece..f084699 100644 --- a/tests/header/test_replaygain.py +++ b/tests/header/test_replaygain.py @@ -1,119 +1,159 @@ # Copyright 2022 (c) Anna Schumaker """Tests our ReplayGain selector.""" import unittest +import unittest.mock import emmental.header.replaygain from gi.repository import Gtk +from gi.repository import Adw -class TestSelector(unittest.TestCase): - """Test case for our custom ReplayGain Selector.""" +class TestCheckRow(unittest.TestCase): + """Test case for our custom CheckRow ListBox row.""" def setUp(self): """Set up common variables.""" - self.selector = emmental.header.replaygain.Selector() + self.checkrow = emmental.header.replaygain.CheckRow("test mode", + title="My Title") - def test_selector(self): - """Check that the Selector is set up properly.""" - self.assertIsInstance(self.selector, Gtk.Grid) - self.assertEqual(self.selector.get_column_spacing(), 6) - self.assertEqual(self.selector.get_margin_top(), 8) + def test_init(self): + """Test that the CheckRow is set up properly.""" + self.assertIsInstance(self.checkrow, Adw.ActionRow) + self.assertIsInstance(self.checkrow._prefix, Gtk.CheckButton) - self.assertEqual(self.selector.mode, "auto") - self.assertFalse(self.selector.enabled) + self.assertEqual(self.checkrow.mode, "test mode") + self.assertEqual(self.checkrow.get_title(), "My Title") + self.assertEqual(self.checkrow.get_activatable_widget(), + self.checkrow._prefix) - self.selector.enabled = True - self.assertTrue(self.selector._switch.get_state()) - self.selector.enabled = False - self.assertFalse(self.selector._switch.get_state()) + def test_active(self): + """Test the CheckRow active property.""" + self.assertFalse(self.checkrow.active) - def test_title(self): - """Check that the Selector title label is set up properly.""" - self.assertIsInstance(self.selector._title, Gtk.Label) - self.assertEqual(self.selector.get_child_at(0, 0), - self.selector._title) + self.checkrow.active = True + self.assertTrue(self.checkrow._prefix.get_active()) + self.checkrow._prefix.set_active(False) + self.assertFalse(self.checkrow.active) - self.assertEqual(self.selector._title.get_text(), - "Volume Normalization") - self.assertAlmostEqual(self.selector._title.get_yalign(), 0.8) - self.assertTrue(self.selector._title.has_css_class("title-4")) - self.assertTrue(self.selector._title.get_hexpand()) - self.assertTrue(self.selector._title.get_vexpand()) + checkrow2 = emmental.header.replaygain.CheckRow("other", active=True) + self.assertTrue(checkrow2.active) + self.assertTrue(checkrow2._prefix.get_active()) + + def test_group(self): + """Test the CheckRow group property.""" + self.assertIsNone(self.checkrow.group) + checkrow2 = emmental.header.replaygain.CheckRow("other", + group=self.checkrow) + self.assertEqual(checkrow2.group, self.checkrow) + + def test_set_active(self): + """Test the set_active() property.""" + notify = unittest.mock.Mock() + self.checkrow.connect("notify::active", notify) + + self.checkrow.set_active(True) + self.assertTrue(self.checkrow.active) + notify.assert_called() + + notify.reset_mock() + self.checkrow.set_active(True) + notify.assert_not_called() + + +class TestReplayGainRow(unittest.TestCase): + """Test case for our custom ReplayGain ListBox row.""" + + def setUp(self): + """Set up common variables.""" + self.replaygain = emmental.header.replaygain.ReplayGainRow() + + def test_init(self): + """Check that the ReplayGainRow is set up properly.""" + self.assertIsInstance(self.replaygain, Adw.ExpanderRow) + self.assertEqual(self.replaygain.get_title(), "Volume Normalization") + self.assertEqual(self.replaygain.get_subtitle(), + "Configure ReplayGain normalizing") + + self.assertEqual(self.replaygain.mode, "auto") + self.assertFalse(self.replaygain.enabled) + + self.replaygain.set_expanded(True) + self.assertTrue(self.replaygain._switch.get_active()) + self.replaygain.set_expanded(False) + self.assertFalse(self.replaygain._switch.get_active()) def test_switch(self): - """Check that the Selector switch works as intended.""" - self.assertIsInstance(self.selector._switch, Gtk.Switch) - self.assertEqual(self.selector.get_child_at(1, 0), - self.selector._switch) - self.assertFalse(self.selector._switch.get_active()) + """Check that the ReplayGainRow switch works as intended.""" + self.assertIsInstance(self.replaygain._switch, Gtk.Switch) + self.assertEqual(self.replaygain._switch.get_valign(), + Gtk.Align.CENTER) + self.assertFalse(self.replaygain._switch.get_active()) - self.selector._switch.set_active(True) - self.assertTrue(self.selector.enabled) - self.assertTrue(self.selector._auto.get_sensitive()) - self.assertTrue(self.selector._album.get_sensitive()) - self.assertTrue(self.selector._track.get_sensitive()) - - self.selector._switch.set_active(False) - self.assertFalse(self.selector.enabled) - self.assertFalse(self.selector._auto.get_sensitive()) - self.assertFalse(self.selector._album.get_sensitive()) - self.assertFalse(self.selector._track.get_sensitive()) + self.replaygain._switch.set_active(True) + self.assertTrue(self.replaygain.get_expanded()) + self.replaygain._switch.set_active(False) + self.assertFalse(self.replaygain.get_expanded()) def test_automatic_mode(self): - """Test the Selector automatic mode button.""" - self.assertIsInstance(self.selector._auto, Gtk.CheckButton) - self.assertEqual(self.selector.get_child_at(0, 1), self.selector._auto) + """Test the ReplayGainRow automatic mode option.""" + self.assertIsInstance(self.replaygain._automatic, + emmental.header.replaygain.CheckRow) + self.assertEqual(self.replaygain._automatic.get_title(), + "Automatic Mode") + self.assertEqual(self.replaygain._automatic.get_subtitle(), + "Emmental decides automatically") - self.assertEqual(self.selector._auto.get_label(), - "Decide automatically") - self.assertFalse(self.selector._auto.get_sensitive()) - self.assertTrue(self.selector._auto.get_active()) + self.assertEqual(self.replaygain._automatic.mode, "auto") + self.assertTrue(self.replaygain._automatic.active) - self.selector._track.set_active(True) - self.selector._auto.set_active(True) - self.assertEqual(self.selector.mode, "auto") + self.replaygain._track.active = True + self.replaygain._automatic.active = True + self.assertEqual(self.replaygain.mode, "auto") + self.assertFalse(self.replaygain._track.active) def test_album_mode(self): - """Test the Selector album mode button.""" - self.assertIsInstance(self.selector._album, Gtk.CheckButton) - self.assertEqual(self.selector.get_child_at(0, 2), - self.selector._album) - - self.assertEqual(self.selector._album.get_label(), + """Test the ReplayGainRow album mode option.""" + self.assertIsInstance(self.replaygain._album, + emmental.header.replaygain.CheckRow) + self.assertEqual(self.replaygain._album.get_title(), "Album Mode") + self.assertEqual(self.replaygain._album.get_subtitle(), "Albums have the same volume") - self.assertFalse(self.selector._album.get_sensitive()) - self.assertFalse(self.selector._album.get_active()) - self.selector._album.set_active(True) - self.assertEqual(self.selector.mode, "album") + self.assertEqual(self.replaygain._album.mode, "album") + self.assertEqual(self.replaygain._album.group, + self.replaygain._automatic) + + self.replaygain._album.active = True + self.assertEqual(self.replaygain.mode, "album") def test_track_mode(self): - """Test the Selector album mode button.""" - self.assertIsInstance(self.selector._track, Gtk.CheckButton) - self.assertEqual(self.selector.get_child_at(0, 3), - self.selector._track) - - self.assertEqual(self.selector._track.get_label(), + """Test the ReplayGainRow track mode option.""" + self.assertIsInstance(self.replaygain._track, + emmental.header.replaygain.CheckRow) + self.assertEqual(self.replaygain._track.get_title(), "Track Mode") + self.assertEqual(self.replaygain._track.get_subtitle(), "Tracks have the same volume") - self.assertFalse(self.selector._track.get_sensitive()) - self.assertFalse(self.selector._track.get_active()) - self.selector._track.set_active(True) - self.assertEqual(self.selector.mode, "track") + self.assertEqual(self.replaygain._track.mode, "track") + self.assertEqual(self.replaygain._track.group, + self.replaygain._automatic) + + self.replaygain._track.active = True + self.assertEqual(self.replaygain.mode, "track") def test_mode_property(self): """Test that the mode property is set correctly.""" - self.selector.mode = "album" - self.assertTrue(self.selector._album.get_active()) - self.assertFalse(self.selector._auto.get_active()) - self.assertFalse(self.selector._track.get_active()) + self.replaygain.mode = "album" + self.assertTrue(self.replaygain._album.active) + self.assertFalse(self.replaygain._automatic.active) + self.assertFalse(self.replaygain._track.active) - self.selector.mode = "track" - self.assertTrue(self.selector._track.get_active()) - self.assertFalse(self.selector._auto.get_active()) - self.assertFalse(self.selector._album.get_active()) + self.replaygain.mode = "track" + self.assertTrue(self.replaygain._track.active) + self.assertFalse(self.replaygain._automatic.active) + self.assertFalse(self.replaygain._album.active) - self.selector.mode = "anything else" - self.assertTrue(self.selector._auto.get_active()) - self.assertFalse(self.selector._album.get_active()) - self.assertFalse(self.selector._track.get_active()) - self.assertEqual(self.selector.mode, "auto") + self.replaygain.mode = "anything else" + self.assertTrue(self.replaygain._automatic.active) + self.assertFalse(self.replaygain._album.active) + self.assertFalse(self.replaygain._track.active) + self.assertEqual(self.replaygain.mode, "auto")