header/open: Create an Open button
This button will be used by the user to open and play a track from the filesystem. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
bb1d01f951
commit
5d0d522e3c
|
@ -1,10 +1,12 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""A custom Gtk.HeaderBar configured for our application."""
|
||||
import pathlib
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Adw
|
||||
from .. import db
|
||||
from .. import buttons
|
||||
from . import open
|
||||
from . import replaygain
|
||||
from . import volume
|
||||
if __debug__:
|
||||
|
@ -36,6 +38,7 @@ class Header(Gtk.HeaderBar):
|
|||
def __init__(self, sql: db.Connection, title: str):
|
||||
"""Initialize the HeaderBar."""
|
||||
super().__init__(title=title, subtitle=SUBTITLE, sql=sql)
|
||||
self._open = open.Button()
|
||||
self._title = Adw.WindowTitle(title=self.title, subtitle=self.subtitle)
|
||||
self._volume = volume.Controls()
|
||||
self._replaygain = replaygain.Selector()
|
||||
|
@ -58,6 +61,7 @@ class Header(Gtk.HeaderBar):
|
|||
self.bind_property("volume", self._volume, "volume",
|
||||
GObject.BindingFlags.BIDIRECTIONAL)
|
||||
|
||||
self.pack_start(self._open)
|
||||
if __debug__:
|
||||
self._window = settings.Window(sql)
|
||||
self._settings = Gtk.Button.new_from_icon_name("settings-symbolic")
|
||||
|
@ -66,6 +70,8 @@ class Header(Gtk.HeaderBar):
|
|||
|
||||
self.pack_end(self._button)
|
||||
self.set_title_widget(self._title)
|
||||
|
||||
self._open.connect("track-requested", self.__track_requested)
|
||||
self.connect("notify::volume", self.__notify_volume)
|
||||
|
||||
def __run_settings(self, button: Gtk.Button) -> None:
|
||||
|
@ -74,3 +80,11 @@ class Header(Gtk.HeaderBar):
|
|||
|
||||
def __notify_volume(self, header, param) -> None:
|
||||
self._button.set_icon_name(_volume_icon(self.volume))
|
||||
|
||||
def __track_requested(self, button: open.Button,
|
||||
path: pathlib.Path) -> None:
|
||||
self.emit("track-requested", path)
|
||||
|
||||
@GObject.Signal(arg_types=(GObject.TYPE_PYOBJECT,))
|
||||
def track_requested(self, path: pathlib.Path) -> None:
|
||||
"""Signal that a track has been requested."""
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""A custom Button that opens a FileDialog to select a file for playback."""
|
||||
import pathlib
|
||||
from gi.repository import GObject
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gio
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class Button(Gtk.Button):
|
||||
"""Our pre-configured open button."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize our open button."""
|
||||
super().__init__(icon_name="document-open-symbolic")
|
||||
self._filters = Gio.ListStore()
|
||||
self._filter = Gtk.FileFilter(name="Audio Files",
|
||||
mime_types=["inode/directory",
|
||||
"audio/*"])
|
||||
self._dialog = Gtk.FileDialog(filters=self._filters,
|
||||
title="Pick a Track")
|
||||
|
||||
self._filters.append(self._filter)
|
||||
|
||||
def __async_ready(self, dialog: Gtk.FileDialog, task: Gio.Task) -> None:
|
||||
try:
|
||||
file = dialog.open_finish(task)
|
||||
self.emit("track-requested", pathlib.Path(file.get_path()))
|
||||
except GLib.Error:
|
||||
pass
|
||||
|
||||
def do_clicked(self) -> None:
|
||||
"""Handle a click event."""
|
||||
self._dialog.open(self.get_ancestor(Gtk.Window), None,
|
||||
self.__async_ready)
|
||||
|
||||
@GObject.Signal(arg_types=(GObject.TYPE_PYOBJECT,))
|
||||
def track_requested(self, file: pathlib.Path) -> None:
|
||||
"""Signal that a track has been requested."""
|
|
@ -2,6 +2,7 @@
|
|||
"""Tests our application header."""
|
||||
import emmental
|
||||
import tests.util
|
||||
import pathlib
|
||||
import unittest.mock
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Adw
|
||||
|
@ -32,6 +33,15 @@ class TestHeader(tests.util.TestCase):
|
|||
self.assertEqual(self.header._title.get_subtitle(),
|
||||
emmental.header.SUBTITLE)
|
||||
|
||||
def test_open(self):
|
||||
"""Check that the Open button works as expected."""
|
||||
self.assertIsInstance(self.header._open, emmental.header.open.Button)
|
||||
|
||||
signal = unittest.mock.Mock()
|
||||
self.header.connect("track-requested", signal)
|
||||
self.header._open.emit("track-requested", pathlib.Path("/a/b/c/1.ogg"))
|
||||
signal.assert_called_with(self.header, pathlib.Path("/a/b/c/1.ogg"))
|
||||
|
||||
def test_settings(self):
|
||||
"""Check that the Settings window is set up correctly."""
|
||||
self.assertIsInstance(self.header._settings, Gtk.Button)
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2023 (c) Anna Schumaker.
|
||||
"""Tests our Open button."""
|
||||
import emmental.header.open
|
||||
import pathlib
|
||||
import unittest
|
||||
from gi.repository import Gio
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class TestButton(unittest.TestCase):
|
||||
"""Test the Open button."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.button = emmental.header.open.Button()
|
||||
|
||||
def test_button(self):
|
||||
"""Check that the button was set up properly."""
|
||||
self.assertIsInstance(self.button, Gtk.Button)
|
||||
self.assertEqual(self.button.get_icon_name(), "document-open-symbolic")
|
||||
|
||||
def test_filter(self):
|
||||
"""Check that the file filter is set up properly."""
|
||||
self.assertIsInstance(self.button._filter, Gtk.FileFilter)
|
||||
self.assertIsInstance(self.button._filters, Gio.ListStore)
|
||||
|
||||
self.assertEqual(self.button._filter.get_name(), "Audio Files")
|
||||
self.assertEqual(self.button._filters[0], self.button._filter)
|
||||
|
||||
def test_dialog(self):
|
||||
"""Check that the file dialog is set up properly."""
|
||||
self.assertIsInstance(self.button._dialog, Gtk.FileDialog)
|
||||
self.assertEqual(self.button._dialog.get_title(), "Pick a Track")
|
||||
self.assertEqual(self.button._dialog.get_filters(),
|
||||
self.button._filters)
|
||||
self.assertTrue(self.button._dialog.get_modal())
|
||||
|
||||
def test_clicked(self):
|
||||
"""Test clicking on the button."""
|
||||
with unittest.mock.patch.object(self.button._dialog,
|
||||
"open") as mock_open:
|
||||
self.button.emit("clicked")
|
||||
mock_open.assert_called_with(None, None,
|
||||
self.button._Button__async_ready)
|
||||
|
||||
with unittest.mock.patch.object(self.button._dialog,
|
||||
"open_finish") as mock_finish:
|
||||
task = Gio.Task()
|
||||
signal = unittest.mock.Mock()
|
||||
mock_finish.return_value = Gio.File.new_for_path("/a/b/c/1.ogg")
|
||||
self.button.connect("track-requested", signal)
|
||||
|
||||
self.button._Button__async_ready(self.button._dialog, task)
|
||||
mock_finish.assert_called_with(task)
|
||||
signal.assert_called_with(self.button,
|
||||
pathlib.Path("/a/b/c/1.ogg"))
|
Loading…
Reference in New Issue