from pathlib import Path import PIL.Image import pytest import toga from toga_gtk.keys import gtk_accel, toga_key from toga_gtk.libs import Gdk, Gtk from .probe import BaseProbe class AppProbe(BaseProbe): supports_key = True supports_key_mod3 = True def __init__(self, app): super().__init__() self.app = app assert isinstance(self.app._impl.native, Gtk.Application) @property def config_path(self): return Path.home() / ".config/testbed" @property def data_path(self): return Path.home() / ".local/share/testbed" @property def cache_path(self): return Path.home() / ".cache/testbed" @property def logs_path(self): return Path.home() / ".local/state/testbed/log" @property def is_cursor_visible(self): pytest.skip("Cursor visibility not implemented on GTK") def is_full_screen(self, window): return bool( window._impl.native.get_window().get_state() & Gdk.WindowState.FULLSCREEN ) def content_size(self, window): content_allocation = window._impl.container.get_allocation() return (content_allocation.width, content_allocation.height) def assert_app_icon(self, icon): for window in self.app.windows: # We have no real way to check we've got the right icon; use pixel peeping as a # guess. Construct a PIL image from the current icon. img = toga.Image(window._impl.native.get_icon()).as_format(PIL.Image.Image) if icon: # The explicit alt icon has blue background, with green at a point 1/3 into # the image assert img.getpixel((5, 5)) == (211, 230, 245) mid_color = img.getpixel((img.size[0] // 3, img.size[1] // 3)) assert mid_color == (0, 204, 9) else: # The default icon is transparent background, and brown in the center. assert img.getpixel((5, 5))[3] == 0 mid_color = img.getpixel((img.size[0] // 2, img.size[1] // 2)) assert mid_color == (149, 119, 73, 255) def _menu_item(self, path): main_menu = self.app._impl.native.get_menubar() menu = main_menu orig_path = path.copy() try: while True: label, path = path[0], path[1:] items = {} for index in range(menu.get_n_items()): section = menu.get_item_link(index, "section") if section: for section_index in range(section.get_n_items()): items[ section.get_item_attribute_value( section_index, "label" ).get_string() ] = (section, section_index) else: items[ menu.get_item_attribute_value(index, "label").get_string() ] = (menu, index) if label == "*": item = items[self.app.formal_name] else: item = items[label] menu = item[0].get_item_link(item[1], "submenu") except IndexError: pass except AttributeError: raise AssertionError(f"Menu {' > '.join(orig_path)} not found") action = item[0].get_item_attribute_value(item[1], "action") if action: action_name = ( item[0].get_item_attribute_value(item[1], "action").get_string() ) cmd_id = action_name.split(".")[1] action = self.app._impl.native.lookup_action(cmd_id) return item, action def _activate_menu_item(self, path): _, action = self._menu_item(path) action.emit("activate", None) def activate_menu_exit(self): self._activate_menu_item(["*", "Quit Toga Testbed"]) def activate_menu_about(self): self._activate_menu_item(["Help", "About Toga Testbed"]) async def close_about_dialog(self): self.app._impl._close_about(self.app._impl.native_about_dialog) def activate_menu_visit_homepage(self): # Homepage is a link on the GTK about page. pytest.xfail("GTK doesn't have a visit homepage menu item") def assert_system_menus(self): self.assert_menu_item(["*", "Preferences"], enabled=False) self.assert_menu_item(["*", "Quit Toga Testbed"], enabled=True) self.assert_menu_item(["Help", "About Toga Testbed"], enabled=True) def activate_menu_close_window(self): pytest.xfail("GTK doesn't have a window management menu items") def activate_menu_close_all_windows(self): pytest.xfail("GTK doesn't have a window management menu items") def activate_menu_minimize(self): pytest.xfail("GTK doesn't have a window management menu items") def assert_menu_item(self, path, enabled): _, action = self._menu_item(path) assert action.get_enabled() == enabled def assert_menu_order(self, path, expected): item, action = self._menu_item(path) menu = item[0].get_item_link(item[1], "submenu") # Loop over the sections actual = [] for index in range(menu.get_n_items()): section = menu.get_item_link(index, "section") if section: if actual: actual.append("---") for section_index in range(section.get_n_items()): actual.append( section.get_item_attribute_value( section_index, "label" ).get_string() ) else: actual.append( section.get_item_attribute_value(index, "label").get_string() ) assert actual == expected def keystroke(self, combination): accel = gtk_accel(combination) state = 0 if "" in accel: state |= Gdk.ModifierType.CONTROL_MASK accel = accel.replace("", "") if "" in accel: state |= Gdk.ModifierType.META_MASK accel = accel.replace("", "") if "" in accel: state |= Gdk.ModifierType.HYPER_MASK accel = accel.replace("", "") if "" in accel: state |= Gdk.ModifierType.SHIFT_MASK accel = accel.replace("", "") keyval = getattr( Gdk, f"KEY_{accel}", { "!": Gdk.KEY_exclam, "": Gdk.KEY_Home, "F5": Gdk.KEY_F5, }.get(accel, None), ) event = Gdk.Event.new(Gdk.EventType.KEY_PRESS) event.keyval = keyval event.length = 1 event.is_modifier = state != 0 event.state = state return toga_key(event)