diff --git a/emmental/header/replaygain.py b/emmental/header/replaygain.py new file mode 100644 index 0000000..08e65ea --- /dev/null +++ b/emmental/header/replaygain.py @@ -0,0 +1,53 @@ +# Copyright 2022 (c) Anna Schumaker. +"""A widget for selecting ReplayGain mode.""" +from gi.repository import GObject +from gi.repository import Gtk + + +class Selector(Gtk.Grid): + """Build up a widget for configuring ReplayGain settings.""" + + enabled = GObject.Property(type=bool, default=False) + mode = GObject.Property(type=str, default="auto") + + 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) + + 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.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._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", + GObject.BindingFlags.BIDIRECTIONAL) + + self._title.add_css_class("title-4") + + def __notify_mode(self, selector: Gtk.Grid, param) -> None: + match selector.get_property("mode"): + case "album": self._album.set_active(True) + case "track": self._track.set_active(True) + case _: self._auto.set_active(True) + + def __mode_toggled(self, check: Gtk.CheckButton, new_mode: str) -> None: + if check.get_active(): + self.mode = new_mode diff --git a/tests/header/test_replaygain.py b/tests/header/test_replaygain.py new file mode 100644 index 0000000..3ea9ece --- /dev/null +++ b/tests/header/test_replaygain.py @@ -0,0 +1,119 @@ +# Copyright 2022 (c) Anna Schumaker +"""Tests our ReplayGain selector.""" +import unittest +import emmental.header.replaygain +from gi.repository import Gtk + + +class TestSelector(unittest.TestCase): + """Test case for our custom ReplayGain Selector.""" + + def setUp(self): + """Set up common variables.""" + self.selector = emmental.header.replaygain.Selector() + + 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) + + self.assertEqual(self.selector.mode, "auto") + self.assertFalse(self.selector.enabled) + + self.selector.enabled = True + self.assertTrue(self.selector._switch.get_state()) + self.selector.enabled = False + self.assertFalse(self.selector._switch.get_state()) + + 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.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()) + + 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()) + + 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()) + + 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) + + self.assertEqual(self.selector._auto.get_label(), + "Decide automatically") + self.assertFalse(self.selector._auto.get_sensitive()) + self.assertTrue(self.selector._auto.get_active()) + + self.selector._track.set_active(True) + self.selector._auto.set_active(True) + self.assertEqual(self.selector.mode, "auto") + + 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(), + "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") + + 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(), + "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") + + 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.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.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")