header: Add new volume control widgets to the Header class
They live in a Gtk.MenuButton with a custom popover box that can have replaygain options added to it. I also modify the Application to save the volume when it is changed. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
847c15f64b
commit
081cae4cd8
|
@ -30,7 +30,9 @@ class Application(Adw.Application):
|
|||
|
||||
def build_header(self) -> header.Header:
|
||||
"""Build a new header instance."""
|
||||
return header.Header(sql=self.db, title=VERSION_STRING)
|
||||
hdr = header.Header(sql=self.db, title=VERSION_STRING)
|
||||
self.db.settings.bind_setting("audio.volume", hdr, "volume")
|
||||
return hdr
|
||||
|
||||
def build_window(self) -> window.Window:
|
||||
"""Build a new window instance."""
|
||||
|
|
|
@ -4,26 +4,49 @@ from gi.repository import GObject
|
|||
from gi.repository import Gtk
|
||||
from gi.repository import Adw
|
||||
from .. import db
|
||||
from .. import buttons
|
||||
from . import volume
|
||||
if __debug__:
|
||||
from . import settings
|
||||
|
||||
SUBTITLE = "The Cheesy Music Player"
|
||||
|
||||
|
||||
def _volume_icon(vol: float) -> str:
|
||||
if vol == 0.0:
|
||||
return "audio-volume-muted-symbolic"
|
||||
if vol <= 1/3:
|
||||
return "audio-volume-low-symbolic"
|
||||
if vol <= 2/3:
|
||||
return "audio-volume-medium-symbolic"
|
||||
return "audio-volume-high-symbolic"
|
||||
|
||||
|
||||
class Header(Gtk.HeaderBar):
|
||||
"""Our custom Gtk.HeaderBar containing window title and volume controls."""
|
||||
|
||||
sql = GObject.Property(type=db.Connection)
|
||||
title = GObject.Property(type=str)
|
||||
subtitle = GObject.Property(type=str)
|
||||
volume = GObject.Property(type=float, default=1.0)
|
||||
|
||||
def __init__(self, sql: db.Connection, title: str):
|
||||
"""Initialize the HeaderBar."""
|
||||
super().__init__(title=title, subtitle=SUBTITLE, sql=sql)
|
||||
self._title = Adw.WindowTitle(title=self.title, subtitle=self.subtitle)
|
||||
self._volume = volume.Controls()
|
||||
|
||||
self._box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
||||
self._box.append(self._volume)
|
||||
|
||||
icon = _volume_icon(self.volume)
|
||||
self._button = buttons.PopoverButton(popover_child=self._box,
|
||||
icon_name=icon)
|
||||
|
||||
self.bind_property("title", self._title, "title")
|
||||
self.bind_property("subtitle", self._title, "subtitle")
|
||||
self.bind_property("volume", self._volume, "volume",
|
||||
GObject.BindingFlags.BIDIRECTIONAL)
|
||||
|
||||
if __debug__:
|
||||
self._window = settings.Window(sql)
|
||||
|
@ -31,8 +54,13 @@ class Header(Gtk.HeaderBar):
|
|||
self._settings.connect("clicked", self.__run_settings)
|
||||
self.pack_start(self._settings)
|
||||
|
||||
self.pack_end(self._button)
|
||||
self.set_title_widget(self._title)
|
||||
self.connect("notify::volume", self.__notify_volume)
|
||||
|
||||
def __run_settings(self, button: Gtk.Button) -> None:
|
||||
if __debug__:
|
||||
self._window.present()
|
||||
|
||||
def __notify_volume(self, header, param) -> None:
|
||||
self._button.set_icon_name(_volume_icon(self.volume))
|
||||
|
|
|
@ -46,3 +46,40 @@ class TestHeader(tests.util.TestCase):
|
|||
"present") as mock_present:
|
||||
self.header._settings.emit("clicked")
|
||||
mock_present.assert_called()
|
||||
|
||||
def test_volume(self):
|
||||
"""Check that volume widgets work as expected."""
|
||||
self.assertIsInstance(self.header._volume,
|
||||
emmental.header.volume.Controls)
|
||||
self.assertEqual(self.header.volume, 1.0)
|
||||
|
||||
for i, vol in [(x, x/10) for x in range(11)]:
|
||||
with self.subTest(volume=vol):
|
||||
widget = self.header if i % 2 else self.header._volume
|
||||
match i:
|
||||
case 0: icon = "muted"
|
||||
case 1 | 2 | 3: icon = "low"
|
||||
case 4 | 5 | 6: icon = "medium"
|
||||
case _: icon = "high"
|
||||
|
||||
widget.volume = vol
|
||||
self.assertEqual(self.header.volume, vol)
|
||||
self.assertEqual(self.header._volume.volume, vol)
|
||||
self.assertEqual(self.header._button.get_icon_name(),
|
||||
f"audio-volume-{icon}-symbolic")
|
||||
|
||||
def test_popover(self):
|
||||
"""Check that the menu popover was set up correctly."""
|
||||
self.assertIsInstance(self.header._button,
|
||||
emmental.buttons.PopoverButton)
|
||||
self.assertIsInstance(self.header._box, Gtk.Box)
|
||||
|
||||
self.assertEqual(self.header._box.get_orientation(),
|
||||
Gtk.Orientation.VERTICAL)
|
||||
self.assertEqual(self.header._box.get_spacing(), 0)
|
||||
|
||||
self.assertEqual(self.header._button.get_icon_name(),
|
||||
"audio-volume-high-symbolic")
|
||||
self.assertEqual(self.header._button.popover_child, self.header._box)
|
||||
self.assertEqual(self.header._box.get_first_child(),
|
||||
self.header._volume)
|
||||
|
|
|
@ -32,3 +32,11 @@ class TestSettings(unittest.TestCase):
|
|||
|
||||
win = self.app.build_window()
|
||||
self.assertEqual(win.get_default_size(), (100, 200))
|
||||
|
||||
def test_save_volume(self):
|
||||
"""Check saving and loading volume from the database."""
|
||||
self.assertEqual(self.settings["audio.volume"], 1.0)
|
||||
self.win.header.volume = 0.5
|
||||
self.assertEqual(self.settings["audio.volume"], 0.5)
|
||||
|
||||
self.assertEqual(self.app.build_header().volume, 0.5)
|
||||
|
|
Loading…
Reference in New Issue