From c1b73748f1b9c42dce28a537a149612f7737b74d Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 28 Jul 2023 16:59:48 -0400 Subject: [PATCH] gtk: Add a window to the Application This is an empty Adw.Window with a headerbar configured to display the application name. I also bind the Application runid property to the Window to print out the currently displayed run. Signed-off-by: Anna Schumaker --- tests/gtk/test_window.py | 66 ++++++++++++++++++++++++++++++++++++++ tests/test_gtk.py | 31 ++++++++++++++---- xfstestsdb/gtk/__init__.py | 6 ++++ xfstestsdb/gtk/window.py | 38 ++++++++++++++++++++++ 4 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 tests/gtk/test_window.py create mode 100644 xfstestsdb/gtk/window.py diff --git a/tests/gtk/test_window.py b/tests/gtk/test_window.py new file mode 100644 index 0000000..2b16813 --- /dev/null +++ b/tests/gtk/test_window.py @@ -0,0 +1,66 @@ +# Copyright 2023 (c) Anna Schumaker. +"""Tests our Window implementation.""" +import unittest +import xfstestsdb.gtk.window +from gi.repository import Gtk +from gi.repository import Adw + + +class TestWindow(unittest.TestCase): + """Test case for our Window subclass.""" + + def setUp(self): + """Set up common variables.""" + self.window = xfstestsdb.gtk.window.Window() + + def test_init(self): + """Check that the Window is set up correctly.""" + self.assertIsInstance(self.window, Adw.Window) + self.assertEqual(self.window.props.default_height, 800) + self.assertEqual(self.window.props.default_width, 1200) + self.assertTrue(self.window.has_css_class("devel")) + + self.assertIsInstance(self.window.props.content, Gtk.Box) + self.assertEqual(self.window.props.content.get_orientation(), + Gtk.Orientation.VERTICAL) + + def test_headerbar(self): + """Test the headerbar property.""" + self.assertIsInstance(self.window.headerbar, Adw.HeaderBar) + self.assertEqual(self.window.props.content.get_first_child(), + self.window.headerbar) + self.assertTrue(self.window.headerbar.has_css_class("flat")) + + def test_title_widget(self): + """Test the title widget property.""" + self.assertIsInstance(self.window.title, Adw.WindowTitle) + self.assertEqual(self.window.title.props.title, "xfstestsdb gtk") + self.assertEqual(self.window.headerbar.props.title_widget, + self.window.title) + + def test_child(self): + """Test the window child property.""" + self.assertIsInstance(self.window.headerbar.get_next_sibling(), + Adw.Bin) + + self.assertIsNone(self.window.child) + self.window.child = Gtk.Label() + self.assertEqual(self.window.headerbar.get_next_sibling().props.child, + self.window.child) + + label = Gtk.Label() + window2 = xfstestsdb.gtk.window.Window(child=label) + self.assertEqual(window2.child, label) + self.assertEqual(window2.headerbar.get_next_sibling().props.child, + label) + + def test_runid(self): + """Test the window runid property.""" + self.assertEqual(self.window.runid, 0) + self.assertEqual(self.window.title.props.subtitle, "") + + self.window.runid = 3 + self.assertEqual(self.window.title.props.subtitle, "runid #3") + + self.window.runid = 0 + self.assertEqual(self.window.title.props.subtitle, "") diff --git a/tests/test_gtk.py b/tests/test_gtk.py index 00cc6e8..dc991a4 100644 --- a/tests/test_gtk.py +++ b/tests/test_gtk.py @@ -50,17 +50,36 @@ class TestApplication(unittest.TestCase): mock_activate.assert_called() self.assertEqual(self.application.runid, 42) + @unittest.mock.patch("gi.repository.Adw.Application.add_window") @unittest.mock.patch("gi.repository.Adw.Application.do_startup") - def test_startup(self, mock_startup: unittest.mock.Mock): + def test_startup(self, mock_startup: unittest.mock.Mock, + mock_add_window: unittest.mock.Mock): """Check that startup sets up our application instance correctly.""" - self.application.emit("startup") - mock_startup.assert_called_with(self.application) + self.assertIsNone(self.application.win) + self.application.emit("startup") + + self.assertIsInstance(self.application.win, + xfstestsdb.gtk.window.Window) + mock_startup.assert_called_with(self.application) + mock_add_window.assert_called_with(self.application.win) + + self.application.runid = 42 + self.assertEqual(self.application.win.runid, 42) + + @unittest.mock.patch("gi.repository.Adw.Application.add_window") + @unittest.mock.patch("gi.repository.Adw.Application.do_startup") @unittest.mock.patch("gi.repository.Adw.Application.do_activate") - def test_activate(self, mock_activate: unittest.mock.Mock): + def test_activate(self, mock_activate: unittest.mock.Mock, + mock_startup: unittest.mock.Mock, + mock_add_window: unittest.mock.Mock): """Check that activating our application works correctly.""" - self.application.emit("activate") - mock_activate.assert_called_with(self.application) + self.application.emit("startup") + with unittest.mock.patch.object(self.application.win, + "present") as mock_present: + self.application.emit("activate") + mock_activate.assert_called_with(self.application) + mock_present.assert_called() @unittest.mock.patch("gi.repository.Adw.Application.do_shutdown") def test_shutdown(self, mock_shutdown: unittest.mock.Mock): diff --git a/xfstestsdb/gtk/__init__.py b/xfstestsdb/gtk/__init__.py index 7e480ce..9d07b13 100644 --- a/xfstestsdb/gtk/__init__.py +++ b/xfstestsdb/gtk/__init__.py @@ -7,6 +7,7 @@ from . import gsetup from gi.repository import GObject from gi.repository import Gio from gi.repository import Adw +from . import window from .. import commands from .. import sqlite @@ -15,6 +16,7 @@ class Application(Adw.Application): """Our Adw.Application for displaying xfstests results.""" runid = GObject.Property(type=int) + win = GObject.Property(type=window.Window) def __init__(self): """Initialize the application.""" @@ -36,10 +38,14 @@ class Application(Adw.Application): def do_startup(self) -> None: """Handle the Adw.Application::startup signal.""" Adw.Application.do_startup(self) + self.win = window.Window() + self.bind_property("runid", self.win, "runid") + self.add_window(self.win) def do_activate(self) -> None: """Handle the Adw.Application::activate signal.""" Adw.Application.do_activate(self) + self.win.present() def do_shutdown(self) -> None: """Handle the Adw.Application::shutdown signal.""" diff --git a/xfstestsdb/gtk/window.py b/xfstestsdb/gtk/window.py new file mode 100644 index 0000000..aeceb1b --- /dev/null +++ b/xfstestsdb/gtk/window.py @@ -0,0 +1,38 @@ +# Copyright 2023 (c) Anna Schumaker. +"""The main Adw.Window implementation for our application.""" +import typing +from gi.repository import GObject +from gi.repository import Gtk +from gi.repository import Adw + + +class Window(Adw.Window): + """Our customized Window displayed to the user.""" + + child = GObject.Property(type=Gtk.Widget) + headerbar = GObject.Property(type=Adw.HeaderBar) + title = GObject.Property(type=Adw.WindowTitle) + runid = GObject.Property(type=int) + + def __init__(self, **kwargs): + """Set up our Window.""" + super().__init__(default_height=800, default_width=1200, + content=Gtk.Box(orientation=Gtk.Orientation.VERTICAL), + title=Adw.WindowTitle(title="xfstestsdb gtk"), + headerbar=Adw.HeaderBar(), **kwargs) + self.headerbar.props.title_widget = self.title + self.headerbar.add_css_class("flat") + + self.props.content.append(self.headerbar) + self.props.content.append(Adw.Bin(child=self.child)) + + self.bind_property("child", self.headerbar.get_next_sibling(), "child") + self.connect("notify::runid", self.__notify_runid) + + if __debug__: + self.add_css_class("devel") + + def __notify_runid(self, window: typing.Self, + param: GObject.ParamSpec) -> None: + text = f"runid #{self.runid}" if self.runid > 0 else "" + self.title.props.subtitle = text