window: Add a custom Window class
This window is set up with specific areas for our header, sidebar, now playing info, and tracklist. It also implements a post_toast() function so toast notifications can be displayed to the user. Implements: #44 ("Create a new 3WayPane widget") Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
767f0c1584
commit
5d1c11e64e
|
@ -1 +1,11 @@
|
|||
/* Copyright 2022 (c) Anna Schumaker. */
|
||||
|
||||
/* Make the Gtk.Paned separator transparent with extra padding */
|
||||
paned.emmental-pane>separator {
|
||||
opacity: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
*.emmental-padding {
|
||||
padding: 8px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""A custom Adw.Window configured for our application."""
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Adw
|
||||
|
||||
|
||||
def _make_pane(orientation: Gtk.Orientation,
|
||||
start_child: Gtk.Widget = None,
|
||||
end_child: Gtk.Widget = None) -> Gtk.Paned:
|
||||
pane = Gtk.Paned(orientation=orientation, hexpand=True, vexpand=True,
|
||||
shrink_start_child=False, resize_start_child=False,
|
||||
start_child=start_child, end_child=end_child)
|
||||
pane.add_css_class("emmental-pane")
|
||||
return pane
|
||||
|
||||
|
||||
class Window(Adw.Window):
|
||||
"""Our custom Adw.Window.
|
||||
|
||||
Our Window is configured with 4 regions for widgets:
|
||||
* An area to place a CSD header bar
|
||||
* An area for a sidebar
|
||||
* An area to show now-playing information
|
||||
* An area to display a tracklist
|
||||
"""
|
||||
|
||||
header = GObject.Property(type=Gtk.Widget)
|
||||
sidebar = GObject.Property(type=Gtk.Widget)
|
||||
now_playing = GObject.Property(type=Gtk.Widget)
|
||||
tracklist = GObject.Property(type=Gtk.Widget)
|
||||
|
||||
def __init__(self, version: str, **kwargs):
|
||||
"""Initialize our Window."""
|
||||
super().__init__(icon_name="emmental", title=version,
|
||||
default_width=1600, default_height=900, **kwargs)
|
||||
self._box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
|
||||
self._header = Adw.Bin(child=self.header)
|
||||
self._inner_pane = _make_pane(Gtk.Orientation.VERTICAL,
|
||||
start_child=self.now_playing,
|
||||
end_child=self.tracklist)
|
||||
self._outer_pane = _make_pane(Gtk.Orientation.HORIZONTAL,
|
||||
start_child=self.sidebar,
|
||||
end_child=self._inner_pane)
|
||||
self._toast = Adw.ToastOverlay(child=self._outer_pane)
|
||||
|
||||
self._outer_pane.add_css_class("emmental-padding")
|
||||
if __debug__:
|
||||
self.add_css_class("devel")
|
||||
|
||||
self.bind_property("header", self._header, "child")
|
||||
self.bind_property("sidebar", self._outer_pane, "start-child")
|
||||
self.bind_property("now-playing", self._inner_pane, "start-child")
|
||||
self.bind_property("tracklist", self._inner_pane, "end-child")
|
||||
|
||||
self._box.append(self._header)
|
||||
self._box.append(self._toast)
|
||||
self.set_content(self._box)
|
||||
|
||||
def close(self, *args) -> None:
|
||||
"""Close the window."""
|
||||
super().close()
|
||||
|
||||
def post_toast(self, title: str) -> Adw.Toast:
|
||||
"""Create a new Adw.Toas and add it to the window."""
|
||||
toast = Adw.Toast.new(title)
|
||||
self._toast.add_toast(toast)
|
||||
return toast
|
||||
|
||||
def present(self, *args) -> None:
|
||||
"""Present the window."""
|
||||
super().present()
|
|
@ -0,0 +1,125 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""Tests our application window."""
|
||||
import unittest
|
||||
import emmental.window
|
||||
from gi.repository import Adw
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class TestWindow(unittest.TestCase):
|
||||
"""Test case for our custom Gtk.Window."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.window = emmental.window.Window("Test 1.2.3")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up."""
|
||||
self.window.close()
|
||||
|
||||
def test_init(self):
|
||||
"""Check that the window gets set up properly."""
|
||||
self.assertIsInstance(self.window, Adw.Window)
|
||||
self.assertIsInstance(self.window._box, Gtk.Box)
|
||||
self.assertIsInstance(self.window._header, Adw.Bin)
|
||||
self.assertIsInstance(self.window._outer_pane, Gtk.Paned)
|
||||
self.assertIsInstance(self.window._inner_pane, Gtk.Paned)
|
||||
self.assertIsInstance(self.window._toast, Adw.ToastOverlay)
|
||||
self.assertTrue(self.window.has_css_class("devel"))
|
||||
|
||||
self.assertEqual(self.window.get_icon_name(), "emmental")
|
||||
self.assertEqual(self.window.get_title(), "Test 1.2.3")
|
||||
self.assertEqual(self.window.get_default_size(), (1600, 900))
|
||||
|
||||
def test_content(self):
|
||||
"""Check that the Window content is set up properly."""
|
||||
self.assertEqual(self.window._box.get_orientation(),
|
||||
Gtk.Orientation.VERTICAL)
|
||||
self.assertEqual(self.window._box.get_spacing(), 0)
|
||||
self.assertEqual(self.window.get_content(), self.window._box)
|
||||
self.assertEqual(self.window._box.get_first_child(),
|
||||
self.window._header)
|
||||
self.assertEqual(self.window._header.get_next_sibling(),
|
||||
self.window._toast)
|
||||
self.assertEqual(self.window._toast.get_child(),
|
||||
self.window._outer_pane)
|
||||
self.assertEqual(self.window._outer_pane.get_end_child(),
|
||||
self.window._inner_pane)
|
||||
self.assertTrue(self.window._outer_pane.has_css_class(
|
||||
"emmental-padding"))
|
||||
|
||||
subtests = [(self.window._outer_pane, Gtk.Orientation.HORIZONTAL),
|
||||
(self.window._inner_pane, Gtk.Orientation.VERTICAL)]
|
||||
for pane, orientation in subtests:
|
||||
self.assertEqual(pane.get_orientation(), orientation)
|
||||
self.assertFalse(pane.get_shrink_start_child())
|
||||
self.assertFalse(pane.get_resize_start_child())
|
||||
self.assertTrue(pane.get_hexpand())
|
||||
self.assertTrue(pane.get_vexpand())
|
||||
self.assertTrue(pane.has_css_class("emmental-pane"))
|
||||
|
||||
def test_header(self):
|
||||
"""Check setting a widget to the header area."""
|
||||
self.assertIsNone(self.window.header)
|
||||
self.window.header = Gtk.Label()
|
||||
self.assertEqual(self.window._header.get_child(), self.window.header)
|
||||
|
||||
window2 = emmental.window.Window(version="1.2.3", header=Gtk.Label())
|
||||
self.assertIsInstance(window2.header, Gtk.Label)
|
||||
self.assertEqual(window2._header.get_child(), window2.header)
|
||||
|
||||
def test_sidebar(self):
|
||||
"""Check setting a widget to the sidebar area."""
|
||||
self.assertIsNone(self.window.sidebar)
|
||||
self.window.sidebar = Gtk.Label()
|
||||
self.assertEqual(self.window._outer_pane.get_start_child(),
|
||||
self.window.sidebar)
|
||||
|
||||
window2 = emmental.window.Window(version="1.2.3", sidebar=Gtk.Label())
|
||||
self.assertIsInstance(window2.sidebar, Gtk.Label)
|
||||
self.assertEqual(window2._outer_pane.get_start_child(),
|
||||
window2.sidebar)
|
||||
|
||||
def test_now_playing(self):
|
||||
"""Check setting a widget to the now_playing area."""
|
||||
self.assertIsNone(self.window.now_playing)
|
||||
self.window.now_playing = Gtk.Label()
|
||||
self.assertEqual(self.window._inner_pane.get_start_child(),
|
||||
self.window.now_playing)
|
||||
|
||||
window2 = emmental.window.Window(version="1.2.3",
|
||||
now_playing=Gtk.Label())
|
||||
self.assertIsInstance(window2.now_playing, Gtk.Label)
|
||||
self.assertEqual(window2._inner_pane.get_start_child(),
|
||||
window2.now_playing)
|
||||
|
||||
def test_tracklist(self):
|
||||
"""Check setting a widget to the tracklist area."""
|
||||
self.assertIsNone(self.window.tracklist)
|
||||
self.window.tracklist = Gtk.Label()
|
||||
self.assertEqual(self.window._inner_pane.get_end_child(),
|
||||
self.window.tracklist)
|
||||
|
||||
window2 = emmental.window.Window(version="1.2.3",
|
||||
tracklist=Gtk.Label())
|
||||
self.assertIsInstance(window2.tracklist, Gtk.Label)
|
||||
self.assertEqual(window2._inner_pane.get_end_child(),
|
||||
window2.tracklist)
|
||||
|
||||
def test_post_toast(self):
|
||||
"""Test posting a Toast message to the window."""
|
||||
toast = self.window.post_toast("Test Toast")
|
||||
self.assertIsInstance(toast, Adw.Toast)
|
||||
self.assertEqual(toast.get_title(), "Test Toast")
|
||||
|
||||
def test_close(self):
|
||||
"""Test calling close()."""
|
||||
with unittest.mock.patch.object(Gtk.Window, "close") as mock_close:
|
||||
self.window.close(1, 2, 3, 4, 5)
|
||||
mock_close.assert_called()
|
||||
|
||||
def test_present(self):
|
||||
"""Test calling present()."""
|
||||
with unittest.mock.patch.object(Gtk.Window, "present") as mock_present:
|
||||
self.window.present(1, 2, 3, 4, 5)
|
||||
mock_present.assert_called()
|
Loading…
Reference in New Issue