From 286a461b852e9c5d94138091d3173c817d2d045a Mon Sep 17 00:00:00 2001 From: Dan Yeaw Date: Fri, 24 Jan 2025 18:49:39 -0500 Subject: [PATCH] Skip tests not implemented for GTK4 --- gtk/src/toga_gtk/container.py | 6 + gtk/src/toga_gtk/widgets/activityindicator.py | 23 +-- gtk/src/toga_gtk/widgets/canvas.py | 23 +-- gtk/src/toga_gtk/widgets/detailedlist.py | 177 ++++++++++-------- gtk/src/toga_gtk/widgets/imageview.py | 16 +- .../toga_gtk/widgets/multilinetextinput.py | 16 +- gtk/src/toga_gtk/widgets/numberinput.py | 17 +- gtk/src/toga_gtk/widgets/optioncontainer.py | 9 +- gtk/src/toga_gtk/widgets/progressbar.py | 25 +-- gtk/src/toga_gtk/widgets/scrollcontainer.py | 10 +- gtk/src/toga_gtk/widgets/textinput.py | 31 +-- gtk/src/toga_gtk/widgets/tree.py | 55 +++--- gtk/tests_backend/app.py | 30 +++ gtk/tests_backend/dialogs.py | 2 + gtk/tests_backend/widgets/base.py | 5 +- gtk/tests_backend/widgets/canvas.py | 6 +- gtk/tests_backend/widgets/detailedlist.py | 7 +- gtk/tests_backend/widgets/divider.py | 7 +- gtk/tests_backend/widgets/imageview.py | 7 +- gtk/tests_backend/widgets/label.py | 6 +- gtk/tests_backend/widgets/mapview.py | 5 +- .../widgets/multilinetextinput.py | 5 +- gtk/tests_backend/widgets/numberinput.py | 5 +- gtk/tests_backend/widgets/optioncontainer.py | 7 +- gtk/tests_backend/widgets/progressbar.py | 7 +- gtk/tests_backend/widgets/scrollcontainer.py | 7 +- gtk/tests_backend/widgets/selection.py | 6 +- gtk/tests_backend/widgets/slider.py | 7 +- gtk/tests_backend/widgets/splitcontainer.py | 7 +- gtk/tests_backend/widgets/table.py | 5 +- gtk/tests_backend/widgets/textinput.py | 5 +- gtk/tests_backend/widgets/tree.py | 5 +- gtk/tests_backend/widgets/webview.py | 6 +- testbed/tests/app/test_dialogs.py | 7 + testbed/tests/app/test_document_app.py | 3 + testbed/tests/test_icons.py | 3 + testbed/tests/window/test_dialogs.py | 7 + 37 files changed, 370 insertions(+), 205 deletions(-) diff --git a/gtk/src/toga_gtk/container.py b/gtk/src/toga_gtk/container.py index 924c86910b..46d5212df4 100644 --- a/gtk/src/toga_gtk/container.py +++ b/gtk/src/toga_gtk/container.py @@ -123,6 +123,12 @@ def __init__(self): # A flag that can be used to explicitly flag that a redraw is required. self.needs_redraw = True + def get_children(self): + if GTK_VERSION < (4, 0, 0): + return self.get_children() + else: + return None + def refreshed(self): pass diff --git a/gtk/src/toga_gtk/widgets/activityindicator.py b/gtk/src/toga_gtk/widgets/activityindicator.py index 523f7d2d0f..52cab4d9e2 100644 --- a/gtk/src/toga_gtk/widgets/activityindicator.py +++ b/gtk/src/toga_gtk/widgets/activityindicator.py @@ -1,4 +1,4 @@ -from ..libs import Gtk +from ..libs import GTK_VERSION, Gtk from .base import Widget @@ -16,14 +16,15 @@ def stop(self): self.native.stop() def rehint(self): - # print( - # "REHINT", - # self, - # self.native.get_preferred_width(), - # self.native.get_preferred_height(), - # ) - width = self.native.get_preferred_width() - height = self.native.get_preferred_height() + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # print( + # "REHINT", + # self, + # self.native.get_preferred_width(), + # self.native.get_preferred_height(), + # ) + width = self.native.get_preferred_width() + height = self.native.get_preferred_height() - self.interface.intrinsic.width = width[0] - self.interface.intrinsic.height = height[0] + self.interface.intrinsic.width = width[0] + self.interface.intrinsic.height = height[0] diff --git a/gtk/src/toga_gtk/widgets/canvas.py b/gtk/src/toga_gtk/widgets/canvas.py index fe05c28e13..a2f6420b87 100644 --- a/gtk/src/toga_gtk/widgets/canvas.py +++ b/gtk/src/toga_gtk/widgets/canvas.py @@ -8,7 +8,7 @@ from toga.constants import Baseline, FillRule from toga.fonts import SYSTEM_DEFAULT_FONT_SIZE from toga_gtk.colors import native_color -from toga_gtk.libs import Gdk, Gtk, Pango, PangoCairo, cairo +from toga_gtk.libs import GTK_VERSION, Gdk, Gtk, Pango, PangoCairo, cairo from .base import Widget @@ -23,16 +23,17 @@ def create(self): self.native = Gtk.DrawingArea() - self.native.connect("draw", self.gtk_draw_callback) - self.native.connect("size-allocate", self.gtk_on_size_allocate) - self.native.connect("button-press-event", self.mouse_down) - self.native.connect("button-release-event", self.mouse_up) - self.native.connect("motion-notify-event", self.mouse_move) - self.native.set_events( - Gdk.EventMask.BUTTON_PRESS_MASK - | Gdk.EventMask.BUTTON_RELEASE_MASK - | Gdk.EventMask.BUTTON_MOTION_MASK - ) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.connect("draw", self.gtk_draw_callback) + self.native.connect("size-allocate", self.gtk_on_size_allocate) + self.native.connect("button-press-event", self.mouse_down) + self.native.connect("button-release-event", self.mouse_up) + self.native.connect("motion-notify-event", self.mouse_move) + self.native.set_events( + Gdk.EventMask.BUTTON_PRESS_MASK + | Gdk.EventMask.BUTTON_RELEASE_MASK + | Gdk.EventMask.BUTTON_MOTION_MASK + ) def gtk_draw_callback(self, widget, cairo_context): """Creates a draw callback. diff --git a/gtk/src/toga_gtk/widgets/detailedlist.py b/gtk/src/toga_gtk/widgets/detailedlist.py index d9466266a5..817dcff089 100644 --- a/gtk/src/toga_gtk/widgets/detailedlist.py +++ b/gtk/src/toga_gtk/widgets/detailedlist.py @@ -2,7 +2,7 @@ from travertino.size import at_least -from toga_gtk.libs import Gdk, Gio, Gtk, Pango +from toga_gtk.libs import GTK_VERSION, Gdk, Gio, Gtk, Pango from .base import Widget @@ -18,8 +18,10 @@ def __init__(self, dl, row): # The row is a built as a stack, so that the action buttons can be pushed onto # the stack as required. self.stack = Gtk.Stack() - self.stack.set_homogeneous(True) - self.add(self.stack) + + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.stack.set_homogeneous(True) + self.add(self.stack) self.content = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) @@ -28,20 +30,21 @@ def __init__(self, dl, row): self.text = Gtk.Label(xalign=0) - # The three line below are necessary for right to left text. - self.text.set_hexpand(True) - self.text.set_ellipsize(Pango.EllipsizeMode.END) - self.text.set_margin_end(12) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # The three line below are necessary for right to left text. + self.text.set_hexpand(True) + self.text.set_ellipsize(Pango.EllipsizeMode.END) + self.text.set_margin_end(12) - self.content.pack_end(self.text, True, True, 5) + self.content.pack_end(self.text, True, True, 5) - # Update the content for the row. - self.update(dl, row) + # Update the content for the row. + self.update(dl, row) - self.stack.add_named(self.content, "content") + self.stack.add_named(self.content, "content") - # Make sure the widgets have been made visible. - self.show_all() + # Make sure the widgets have been made visible. + self.show_all() def update(self, dl, row): """Update the contents of the rendered row, using data from `row`, @@ -108,99 +111,109 @@ def create(self): # Main functional widget is a ListBox. self.native_detailedlist = Gtk.ListBox() - self.native_detailedlist.set_selection_mode(Gtk.SelectionMode.SINGLE) - self.native_detailedlist.connect("row-selected", self.gtk_on_row_selected) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_detailedlist.set_selection_mode(Gtk.SelectionMode.SINGLE) + self.native_detailedlist.connect("row-selected", self.gtk_on_row_selected) self.store = Gio.ListStore() - # We need to provide a function that transforms whatever is in the store into a - # `Gtk.ListBoxRow`, but the items in the store already are `Gtk.ListBoxRow`, so - # this is the identity function. - self.native_detailedlist.bind_model(self.store, lambda a: a) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # We need to provide a function that transforms whatever is in the + # store into a `Gtk.ListBoxRow`, but the items in the store already + # are `Gtk.ListBoxRow`, so this is the identity function. + self.native_detailedlist.bind_model(self.store, lambda a: a) - # Put the ListBox into a vertically scrolling window. + # Put the ListBox into a vertically scrolling window. scrolled_window = Gtk.ScrolledWindow() - scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) - scrolled_window.set_min_content_width(self.interface._MIN_WIDTH) - scrolled_window.set_min_content_height(self.interface._MIN_HEIGHT) - scrolled_window.add(self.native_detailedlist) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + scrolled_window.set_min_content_width(self.interface._MIN_WIDTH) + scrolled_window.set_min_content_height(self.interface._MIN_HEIGHT) + scrolled_window.add(self.native_detailedlist) self.native_vadj = scrolled_window.get_vadjustment() - self.native_vadj.connect("value-changed", self.gtk_on_value_changed) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_vadj.connect("value-changed", self.gtk_on_value_changed) - # Define a revealer widget that can be used to show/hide with a crossfade. + # Define a revealer widget that can be used to show/hide with a crossfade. self.native_revealer = Gtk.Revealer() - self.native_revealer.set_transition_type(Gtk.RevealerTransitionType.CROSSFADE) - self.native_revealer.set_valign(Gtk.Align.END) - self.native_revealer.set_halign(Gtk.Align.CENTER) - self.native_revealer.set_margin_bottom(12) - self.native_revealer.set_reveal_child(False) - - # Define a refresh button. - self.native_refresh_button = Gtk.Button.new_from_icon_name( - "view-refresh-symbolic", Gtk.IconSize.BUTTON - ) - self.native_refresh_button.set_can_focus(False) - self.native_refresh_button.connect("clicked", self.gtk_on_refresh_clicked) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_revealer.set_transition_type( + Gtk.RevealerTransitionType.CROSSFADE + ) + self.native_revealer.set_valign(Gtk.Align.END) + self.native_revealer.set_halign(Gtk.Align.CENTER) + self.native_revealer.set_margin_bottom(12) + self.native_revealer.set_reveal_child(False) + + # Define a refresh button. + self.native_refresh_button = Gtk.Button.new_from_icon_name( + "view-refresh-symbolic", Gtk.IconSize.BUTTON + ) + self.native_refresh_button.set_can_focus(False) + self.native_refresh_button.connect("clicked", self.gtk_on_refresh_clicked) - style_context = self.native_refresh_button.get_style_context() - style_context.add_class("osd") - style_context.add_class("toga-detailed-list-floating-buttons") - style_context.remove_class("button") + style_context = self.native_refresh_button.get_style_context() + style_context.add_class("osd") + style_context.add_class("toga-detailed-list-floating-buttons") + style_context.remove_class("button") - # Add the refresh button to the revealer - self.native_revealer.add(self.native_refresh_button) + # Add the refresh button to the revealer + self.native_revealer.add(self.native_refresh_button) # The actual native widget is an overlay, made up of the scrolled window, with # the revealer over the top. self.native = Gtk.Overlay() - self.native.add_overlay(scrolled_window) - self.native.add_overlay(self.native_revealer) - - # Set up a gesture to capture right clicks. - self.gesture = Gtk.GestureMultiPress.new(self.native_detailedlist) - self.gesture.set_button(3) - self.gesture.set_propagation_phase(Gtk.PropagationPhase.BUBBLE) - self.gesture.connect("pressed", self.gtk_on_right_click) - - # Set up a box that contains action buttons. This widget can be can be re-used - # for any row when it is activated. - self.native_action_buttons = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - action_buttons_hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) - - # TODO: Can we replace "magic words" like delete with an appropriate icon? - # self.native_primary_action_button = Gtk.Button.new_from_icon_name( - # "user-trash-symbolic", Gtk.IconSize.BUTTON - # ) - action_buttons_hbox.pack_start(Gtk.Box(), True, True, 0) + + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.add_overlay(scrolled_window) + self.native.add_overlay(self.native_revealer) + # Set up a gesture to capture right clicks. + self.gesture = Gtk.GestureMultiPress.new(self.native_detailedlist) + self.gesture.set_button(3) + self.gesture.set_propagation_phase(Gtk.PropagationPhase.BUBBLE) + self.gesture.connect("pressed", self.gtk_on_right_click) + + # Set up a box that contains action buttons. This widget can be re-used + # for any row when it is activated. + self.native_action_buttons = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + action_buttons_hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + + # TODO: Can we replace "magic words" like delete with an appropriate icon? + # self.native_primary_action_button = Gtk.Button.new_from_icon_name( + # "user-trash-symbolic", Gtk.IconSize.BUTTON + # ) + action_buttons_hbox.pack_start(Gtk.Box(), True, True, 0) self.native_primary_action_button = Gtk.Button.new_with_label( self.interface._primary_action ) - self.native_primary_action_button.connect( - "clicked", self.gtk_on_primary_clicked - ) - action_buttons_hbox.pack_start( - self.native_primary_action_button, False, False, 10 - ) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_primary_action_button.connect( + "clicked", self.gtk_on_primary_clicked + ) + action_buttons_hbox.pack_start( + self.native_primary_action_button, False, False, 10 + ) - # TODO: Can we replace "magic words" like delete with an appropriate icon? - # self.native_secondary_action_button = Gtk.Button.new_from_icon_name( - # "user-trash-symbolic", Gtk.IconSize.BUTTON - # ) + # TODO: Can we replace "magic words" like delete with an appropriate icon? + # self.native_secondary_action_button = Gtk.Button.new_from_icon_name( + # "user-trash-symbolic", Gtk.IconSize.BUTTON + # ) self.native_secondary_action_button = Gtk.Button.new_with_label( self.interface._secondary_action ) - self.native_secondary_action_button.connect( - "clicked", self.gtk_on_secondary_clicked - ) - action_buttons_hbox.pack_start( - self.native_secondary_action_button, False, False, 10 - ) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_secondary_action_button.connect( + "clicked", self.gtk_on_secondary_clicked + ) + action_buttons_hbox.pack_start( + self.native_secondary_action_button, False, False, 10 + ) - action_buttons_hbox.pack_start(Gtk.Box(), True, True, 0) + action_buttons_hbox.pack_start(Gtk.Box(), True, True, 0) - self.native_action_buttons.pack_start(action_buttons_hbox, True, False, 0) - self.native_action_buttons.show_all() + self.native_action_buttons.pack_start(action_buttons_hbox, True, False, 0) + self.native_action_buttons.show_all() def row_factory(self, item): return DetailedListRow(self.interface, item) diff --git a/gtk/src/toga_gtk/widgets/imageview.py b/gtk/src/toga_gtk/widgets/imageview.py index 6ca7047e2b..0ef0e9bde0 100644 --- a/gtk/src/toga_gtk/widgets/imageview.py +++ b/gtk/src/toga_gtk/widgets/imageview.py @@ -1,20 +1,24 @@ from toga.widgets.imageview import rehint_imageview -from ..libs import GdkPixbuf, Gtk +from ..libs import GTK_VERSION, GdkPixbuf, Gtk from .base import Widget class ImageView(Widget): def create(self): self.native = Gtk.Image() - self.native.connect("size-allocate", self.gtk_size_allocate) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.connect("size-allocate", self.gtk_size_allocate) self._aspect_ratio = None def set_image(self, image): - if image: - self.set_scaled_pixbuf(image._impl.native, self.native.get_allocation()) - else: - self.native.set_from_pixbuf(None) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + if image: + self.set_scaled_pixbuf(image._impl.native, self.native.get_allocation()) + else: + self.native.set_from_pixbuf(None) + else: # pragma: no-cover-if-gtk3 + self.native.set_from_paintable() def gtk_size_allocate(self, widget, allocation): # GTK doesn't have any native image resizing; so, when the Gtk.Image diff --git a/gtk/src/toga_gtk/widgets/multilinetextinput.py b/gtk/src/toga_gtk/widgets/multilinetextinput.py index b833baf051..bd0a5bc98a 100644 --- a/gtk/src/toga_gtk/widgets/multilinetextinput.py +++ b/gtk/src/toga_gtk/widgets/multilinetextinput.py @@ -1,6 +1,7 @@ from travertino.size import at_least from ..libs import ( + GTK_VERSION, Gtk, get_background_color_css, get_color_css, @@ -34,15 +35,16 @@ def create(self): self.native_textview = Gtk.TextView() self.native_textview.set_name(f"toga-{self.interface.id}-textview") - self.native_textview.get_style_context().add_class("toga") + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native_textview.get_style_context().add_class("toga") - self.native_textview.set_buffer(self.placeholder) - self.native_textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) - self.native_textview.connect("focus-in-event", self.gtk_on_focus_in) - self.native_textview.connect("focus-out-event", self.gtk_on_focus_out) - self.native_textview.connect("key-press-event", self.gtk_on_key_press) + self.native_textview.set_buffer(self.placeholder) + self.native_textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) + self.native_textview.connect("focus-in-event", self.gtk_on_focus_in) + self.native_textview.connect("focus-out-event", self.gtk_on_focus_out) + self.native_textview.connect("key-press-event", self.gtk_on_key_press) - self.native.add(self.native_textview) + self.native.add(self.native_textview) def set_color(self, color): self.apply_css( diff --git a/gtk/src/toga_gtk/widgets/numberinput.py b/gtk/src/toga_gtk/widgets/numberinput.py index 59fc4a3217..bddcc8e0fd 100644 --- a/gtk/src/toga_gtk/widgets/numberinput.py +++ b/gtk/src/toga_gtk/widgets/numberinput.py @@ -5,7 +5,7 @@ from toga.widgets.numberinput import _clean_decimal -from ..libs import Gtk, gtk_text_align +from ..libs import GTK_VERSION, Gtk, gtk_text_align from .base import Widget @@ -64,10 +64,11 @@ def set_text_align(self, value): self.native.set_alignment(xalign) def rehint(self): - width = self.native.get_preferred_width() - height = self.native.get_preferred_height() - - self.interface.intrinsic.width = at_least( - max(self.interface._MIN_WIDTH, width[1]) - ) - self.interface.intrinsic.height = height[1] + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + width = self.native.get_preferred_width() + height = self.native.get_preferred_height() + + self.interface.intrinsic.width = at_least( + max(self.interface._MIN_WIDTH, width[1]) + ) + self.interface.intrinsic.height = height[1] diff --git a/gtk/src/toga_gtk/widgets/optioncontainer.py b/gtk/src/toga_gtk/widgets/optioncontainer.py index 807c6e7c94..c8e8c7f5d0 100644 --- a/gtk/src/toga_gtk/widgets/optioncontainer.py +++ b/gtk/src/toga_gtk/widgets/optioncontainer.py @@ -1,7 +1,7 @@ import asyncio from ..container import TogaContainer -from ..libs import Gtk +from ..libs import GTK_VERSION, Gtk from .base import Widget @@ -29,9 +29,10 @@ def add_option(self, index, text, widget, icon): self.sub_containers.insert(index, sub_container) self.native.insert_page(sub_container, Gtk.Label(label=text), index) - # Tabs aren't visible by default; - # tell the notebook to show all content. - self.native.show_all() + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # Tabs aren't visible by default; + # tell the notebook to show all content. + self.native.show_all() def remove_option(self, index): self.native.remove_page(index) diff --git a/gtk/src/toga_gtk/widgets/progressbar.py b/gtk/src/toga_gtk/widgets/progressbar.py index a44fa71db2..8dcdcceef6 100644 --- a/gtk/src/toga_gtk/widgets/progressbar.py +++ b/gtk/src/toga_gtk/widgets/progressbar.py @@ -2,7 +2,7 @@ from travertino.size import at_least -from ..libs import Gtk +from ..libs import GTK_VERSION, Gtk from .base import Widget # Implementation notes @@ -91,14 +91,15 @@ def stop(self): self._stop_indeterminate() def rehint(self): - # print( - # "REHINT", - # self, - # self.native.get_preferred_width(), - # self.native.get_preferred_height(), - # ) - width = self.native.get_preferred_width() - height = self.native.get_preferred_height() - - self.interface.intrinsic.width = at_least(width[0]) - self.interface.intrinsic.height = height[0] + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # print( + # "REHINT", + # self, + # self.native.get_preferred_width(), + # self.native.get_preferred_height(), + # ) + width = self.native.get_preferred_width() + height = self.native.get_preferred_height() + + self.interface.intrinsic.width = at_least(width[0]) + self.interface.intrinsic.height = height[0] diff --git a/gtk/src/toga_gtk/widgets/scrollcontainer.py b/gtk/src/toga_gtk/widgets/scrollcontainer.py index 33cebbb1fc..743492d5e3 100644 --- a/gtk/src/toga_gtk/widgets/scrollcontainer.py +++ b/gtk/src/toga_gtk/widgets/scrollcontainer.py @@ -1,7 +1,7 @@ from travertino.size import at_least from ..container import TogaContainer -from ..libs import Gtk +from ..libs import GTK_VERSION, Gtk from .base import Widget @@ -21,7 +21,8 @@ def create(self): self.native.set_overlay_scrolling(True) self.document_container = TogaContainer() - self.native.add(self.document_container) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.add(self.document_container) def gtk_on_changed(self, *args): self.interface.on_scroll() @@ -29,8 +30,9 @@ def gtk_on_changed(self, *args): def set_content(self, widget): self.document_container.content = widget - # Force the display of the new content - self.native.show_all() + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # Force the display of the new content + self.native.show_all() def set_app(self, app): self.interface.content.app = app diff --git a/gtk/src/toga_gtk/widgets/textinput.py b/gtk/src/toga_gtk/widgets/textinput.py index f5b9d4a7eb..bd36ffe11b 100644 --- a/gtk/src/toga_gtk/widgets/textinput.py +++ b/gtk/src/toga_gtk/widgets/textinput.py @@ -72,21 +72,22 @@ def set_value(self, value): self.native.set_text(value) def rehint(self): - # print( - # "REHINT", - # self, - # self._impl.get_preferred_width(), - # self._impl.get_preferred_height(), - # getattr(self, "_fixed_height", False), - # getattr(self, "_fixed_width", False), - # ) - width = self.native.get_preferred_width() - height = self.native.get_preferred_height() - - self.interface.intrinsic.width = at_least( - max(self.interface._MIN_WIDTH, width[1]) - ) - self.interface.intrinsic.height = height[1] + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # print( + # "REHINT", + # self, + # self._impl.get_preferred_width(), + # self._impl.get_preferred_height(), + # getattr(self, "_fixed_height", False), + # getattr(self, "_fixed_width", False), + # ) + width = self.native.get_preferred_width() + height = self.native.get_preferred_height() + + self.interface.intrinsic.width = at_least( + max(self.interface._MIN_WIDTH, width[1]) + ) + self.interface.intrinsic.height = height[1] def set_error(self, error_message): self.native.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "error") diff --git a/gtk/src/toga_gtk/widgets/tree.py b/gtk/src/toga_gtk/widgets/tree.py index 9a4a645dcd..cb8ffb4bab 100644 --- a/gtk/src/toga_gtk/widgets/tree.py +++ b/gtk/src/toga_gtk/widgets/tree.py @@ -1,6 +1,6 @@ from travertino.size import at_least -from ..libs import GdkPixbuf, Gtk +from ..libs import GTK_VERSION, GdkPixbuf, Gtk from .base import Widget from .table import TogaRow @@ -14,20 +14,22 @@ def create(self): self.native_tree = Gtk.TreeView(model=self.store) self.native_tree.connect("row-activated", self.gtk_on_row_activated) - self.selection = self.native_tree.get_selection() - if self.interface.multiple_select: - self.selection.set_mode(Gtk.SelectionMode.MULTIPLE) - else: - self.selection.set_mode(Gtk.SelectionMode.SINGLE) - self.selection.connect("changed", self.gtk_on_select) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.selection = self.native_tree.get_selection() + if self.interface.multiple_select: + self.selection.set_mode(Gtk.SelectionMode.MULTIPLE) + else: + self.selection.set_mode(Gtk.SelectionMode.SINGLE) + self.selection.connect("changed", self.gtk_on_select) - self._create_columns() + self._create_columns() self.native = Gtk.ScrolledWindow() - self.native.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) - self.native.add(self.native_tree) - self.native.set_min_content_width(200) - self.native.set_min_content_height(200) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + self.native.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + self.native.add(self.native_tree) + self.native.set_min_content_width(200) + self.native.set_min_content_height(200) def _create_columns(self): if self.interface.headings: @@ -62,24 +64,25 @@ def gtk_on_row_activated(self, widget, path, column): self.interface.on_activate(node=node) def change_source(self, source): - # Temporarily disconnecting the TreeStore improves performance for large - # updates by deferring row rendering until the update is complete. - self.native_tree.set_model(None) + if GTK_VERSION < (4, 0, 0): # pragma: no-cover-if-gtk4 + # Temporarily disconnecting the TreeStore improves performance for large + # updates by deferring row rendering until the update is complete. + self.native_tree.set_model(None) - for column in self.native_tree.get_columns(): - self.native_tree.remove_column(column) - self._create_columns() + for column in self.native_tree.get_columns(): + self.native_tree.remove_column(column) + self._create_columns() - types = [TogaRow] - for accessor in self.interface._accessors: - types.extend([GdkPixbuf.Pixbuf, str]) - self.store = Gtk.TreeStore(*types) + types = [TogaRow] + for accessor in self.interface._accessors: + types.extend([GdkPixbuf.Pixbuf, str]) + self.store = Gtk.TreeStore(*types) - for i, row in enumerate(self.interface.data): - self.insert(None, i, row) + for i, row in enumerate(self.interface.data): + self.insert(None, i, row) - self.native_tree.set_model(self.store) - self.refresh() + self.native_tree.set_model(self.store) + self.refresh() def insert(self, parent, index, item): row = TogaRow(item) diff --git a/gtk/tests_backend/app.py b/gtk/tests_backend/app.py index 0f6f4a00ca..f7332217ad 100644 --- a/gtk/tests_backend/app.py +++ b/gtk/tests_backend/app.py @@ -17,6 +17,10 @@ class AppProbe(BaseProbe, DialogsMixin): supports_key_mod3 = True # Gtk 3.24.41 ships with Ubuntu 24.04 where present() works on Wayland supports_current_window_assignment = not (IS_WAYLAND and GTK_VERSION < (3, 24, 41)) + if GTK_VERSION < (4, 0, 0): + supports_save_dialog = True + else: + supports_save_dialog = False def __init__(self, app): super().__init__() @@ -65,6 +69,8 @@ def assert_app_icon(self, icon): assert mid_color == (149, 119, 73, 255) def assert_dialog_in_focus(self, dialog): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support dialogs") # Gtk.Dialog's methods - is_active(), has_focus() both return False, even # when the dialog is in focus. Hence, they cannot be used to determine focus. assert dialog._impl.native.is_visible(), "The dialog is not in focus" @@ -112,16 +118,24 @@ def _menu_item(self, path): return item, action def _activate_menu_item(self, path): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support system menus") _, action = self._menu_item(path) action.emit("activate", None) def activate_menu_exit(self): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support system menus") self._activate_menu_item(["*", "Quit"]) def activate_menu_about(self): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support system menus") self._activate_menu_item(["Help", "About Toga Testbed"]) async def close_about_dialog(self): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support system menus") self.app._impl._close_about(self.app._impl.native_about_dialog) def activate_menu_visit_homepage(self): @@ -129,6 +143,8 @@ def activate_menu_visit_homepage(self): pytest.xfail("GTK doesn't have a visit homepage menu item") def assert_system_menus(self): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support system menus") self.assert_menu_item(["*", "Preferences"], enabled=False) self.assert_menu_item(["*", "Quit"], enabled=True) @@ -152,10 +168,14 @@ def activate_menu_minimize(self): pytest.xfail("GTK doesn't have a window management menu items") def assert_menu_item(self, path, enabled): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support menu items") _, action = self._menu_item(path) assert action.get_enabled() == enabled def assert_menu_order(self, path, expected): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support menu items") item, action = self._menu_item(path) menu = item[0].get_item_link(item[1], "submenu") @@ -181,6 +201,8 @@ def assert_menu_order(self, path, expected): assert actual == expected def keystroke(self, combination): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support keystroke") accel = gtk_accel(combination) state = 0 @@ -226,9 +248,13 @@ def open_document_by_drag(self, document_path): pytest.xfail("GTK doesn't support opening documents by drag") def has_status_icon(self, status_icon): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support status icons") return status_icon._impl.native is not None def status_menu_items(self, status_icon): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support status menu items") menu = status_icon._impl.native.get_primary_menu() if menu: return [ @@ -244,9 +270,13 @@ def status_menu_items(self, status_icon): return None def activate_status_icon_button(self, item_id): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support status icons") self.app.status_icons[item_id]._impl.native.emit("activate", 0, 0) def activate_status_menu_item(self, item_id, title): + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support status menu items") menu = self.app.status_icons[item_id]._impl.native.get_primary_menu() item = {child.get_label(): child for child in menu.get_children()}[title] diff --git a/gtk/tests_backend/dialogs.py b/gtk/tests_backend/dialogs.py index 5d10694e35..8dee248b8b 100644 --- a/gtk/tests_backend/dialogs.py +++ b/gtk/tests_backend/dialogs.py @@ -196,4 +196,6 @@ def close_handler(dialog, gtk_result): ) def is_modal_dialog(self, dialog): + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Getting the modal of a dialog is not yet supported on GTK4") return dialog._impl.native.get_modal() diff --git a/gtk/tests_backend/widgets/base.py b/gtk/tests_backend/widgets/base.py index 2070762bae..0988c75c84 100644 --- a/gtk/tests_backend/widgets/base.py +++ b/gtk/tests_backend/widgets/base.py @@ -3,7 +3,7 @@ import pytest -from toga_gtk.libs import Gdk, Gtk +from toga_gtk.libs import GTK_VERSION, Gdk, Gtk from ..fonts import FontMixin from ..probe import BaseProbe @@ -22,6 +22,9 @@ def __init__(self, widget): # Set the target for keypress events self._keypress_target = self.native + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 only has minimal container support") + # Ensure that the theme isn't using animations for the widget. settings = Gtk.Settings.get_for_screen(self.native.get_screen()) settings.set_property("gtk-enable-animations", False) diff --git a/gtk/tests_backend/widgets/canvas.py b/gtk/tests_backend/widgets/canvas.py index fd6f639201..31feff9232 100644 --- a/gtk/tests_backend/widgets/canvas.py +++ b/gtk/tests_backend/widgets/canvas.py @@ -1,8 +1,9 @@ from io import BytesIO +import pytest from PIL import Image -from toga_gtk.libs import IS_WAYLAND, Gdk, Gtk +from toga_gtk.libs import GTK_VERSION, IS_WAYLAND, Gdk, Gtk from .base import SimpleProbe @@ -10,6 +11,9 @@ class CanvasProbe(SimpleProbe): native_class = Gtk.DrawingArea + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support a canvas yet") + def reference_variant(self, reference): if reference == "multiline_text": if IS_WAYLAND: diff --git a/gtk/tests_backend/widgets/detailedlist.py b/gtk/tests_backend/widgets/detailedlist.py index e1f11500e3..0bcf924a1f 100644 --- a/gtk/tests_backend/widgets/detailedlist.py +++ b/gtk/tests_backend/widgets/detailedlist.py @@ -1,7 +1,9 @@ import asyncio import html -from toga_gtk.libs import GLib, Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, GLib, Gtk from .base import SimpleProbe @@ -17,6 +19,9 @@ def __init__(self, widget): self.native_vadj = widget._impl.native_vadj assert isinstance(self.native_detailedlist, Gtk.ListBox) + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support a detailed list yet") + @property def row_count(self): return len(self.impl.store) diff --git a/gtk/tests_backend/widgets/divider.py b/gtk/tests_backend/widgets/divider.py index b8dbea842b..7738e9fe26 100644 --- a/gtk/tests_backend/widgets/divider.py +++ b/gtk/tests_backend/widgets/divider.py @@ -1,7 +1,12 @@ -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe class DividerProbe(SimpleProbe): native_class = Gtk.Separator + + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support a divider yet") diff --git a/gtk/tests_backend/widgets/imageview.py b/gtk/tests_backend/widgets/imageview.py index cc0e3e1ad1..383ed2358a 100644 --- a/gtk/tests_backend/widgets/imageview.py +++ b/gtk/tests_backend/widgets/imageview.py @@ -1,4 +1,6 @@ -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -6,6 +8,9 @@ class ImageViewProbe(SimpleProbe): native_class = Gtk.Image + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support image view yet") + @property def preserve_aspect_ratio(self): return self.impl._aspect_ratio is not None diff --git a/gtk/tests_backend/widgets/label.py b/gtk/tests_backend/widgets/label.py index 55b47914c6..49395c8609 100644 --- a/gtk/tests_backend/widgets/label.py +++ b/gtk/tests_backend/widgets/label.py @@ -1,10 +1,14 @@ -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe from .properties import toga_x_text_align, toga_y_text_align class LabelProbe(SimpleProbe): + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Labels are not yet supported on GTK4") native_class = Gtk.Label @property diff --git a/gtk/tests_backend/widgets/mapview.py b/gtk/tests_backend/widgets/mapview.py index bc9b16904f..9d39e309e1 100644 --- a/gtk/tests_backend/widgets/mapview.py +++ b/gtk/tests_backend/widgets/mapview.py @@ -2,7 +2,7 @@ import pytest -from toga_gtk.libs import WebKit2 +from toga_gtk.libs import GTK_VERSION, WebKit2 from .base import SimpleProbe @@ -19,6 +19,9 @@ def region_eq(r1, r2): class MapViewProbe(SimpleProbe): native_class = WebKit2.WebView + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support map view yet") + @property def scale_height(self): return self.height diff --git a/gtk/tests_backend/widgets/multilinetextinput.py b/gtk/tests_backend/widgets/multilinetextinput.py index 0acf22fd42..e68321e2dd 100644 --- a/gtk/tests_backend/widgets/multilinetextinput.py +++ b/gtk/tests_backend/widgets/multilinetextinput.py @@ -1,6 +1,6 @@ import pytest -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe from .properties import toga_color, toga_text_align_from_justification @@ -9,6 +9,9 @@ class MultilineTextInputProbe(SimpleProbe): native_class = Gtk.ScrolledWindow + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support multiline text input yet") + def __init__(self, widget): super().__init__(widget) self.native_textview = self.impl.native_textview diff --git a/gtk/tests_backend/widgets/numberinput.py b/gtk/tests_backend/widgets/numberinput.py index 5029d509d8..687d98bd1d 100644 --- a/gtk/tests_backend/widgets/numberinput.py +++ b/gtk/tests_backend/widgets/numberinput.py @@ -1,7 +1,7 @@ import pytest from toga.constants import JUSTIFY, LEFT -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe from .properties import toga_x_text_align @@ -13,6 +13,9 @@ class NumberInputProbe(SimpleProbe): allows_empty_value = False allows_extra_digits = False + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support number input yet") + def clear_input(self): self.native.set_text("") diff --git a/gtk/tests_backend/widgets/optioncontainer.py b/gtk/tests_backend/widgets/optioncontainer.py index e410006057..c8748bece1 100644 --- a/gtk/tests_backend/widgets/optioncontainer.py +++ b/gtk/tests_backend/widgets/optioncontainer.py @@ -1,4 +1,6 @@ -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -8,6 +10,9 @@ class OptionContainerProbe(SimpleProbe): max_tabs = None disabled_tab_selectable = False + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support option containers yet") + def repaint_needed(self): return ( self.impl.sub_containers[self.native.get_current_page()].needs_redraw diff --git a/gtk/tests_backend/widgets/progressbar.py b/gtk/tests_backend/widgets/progressbar.py index beec388c5d..e52e745fba 100644 --- a/gtk/tests_backend/widgets/progressbar.py +++ b/gtk/tests_backend/widgets/progressbar.py @@ -1,6 +1,8 @@ import asyncio -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -8,6 +10,9 @@ class ProgressBarProbe(SimpleProbe): native_class = Gtk.ProgressBar + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support progress bars yet") + @property def is_determinate(self): return self.widget._impl._max is not None diff --git a/gtk/tests_backend/widgets/scrollcontainer.py b/gtk/tests_backend/widgets/scrollcontainer.py index 364f17c9e9..55a4c766d3 100644 --- a/gtk/tests_backend/widgets/scrollcontainer.py +++ b/gtk/tests_backend/widgets/scrollcontainer.py @@ -1,4 +1,6 @@ -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -7,6 +9,9 @@ class ScrollContainerProbe(SimpleProbe): native_class = Gtk.ScrolledWindow scrollbar_inset = 0 + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support progress bars yet") + @property def has_content(self): return self.impl.document_container.content is not None diff --git a/gtk/tests_backend/widgets/selection.py b/gtk/tests_backend/widgets/selection.py index 3c19cc188f..aa8e2e6576 100644 --- a/gtk/tests_backend/widgets/selection.py +++ b/gtk/tests_backend/widgets/selection.py @@ -1,6 +1,7 @@ +import pytest from pytest import skip, xfail -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -8,6 +9,9 @@ class SelectionProbe(SimpleProbe): native_class = Gtk.ComboBoxText + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support selection probes yet") + def assert_resizes_on_content_change(self): pass diff --git a/gtk/tests_backend/widgets/slider.py b/gtk/tests_backend/widgets/slider.py index dfbd05a50b..c8e8788d64 100644 --- a/gtk/tests_backend/widgets/slider.py +++ b/gtk/tests_backend/widgets/slider.py @@ -1,4 +1,6 @@ -from toga_gtk.libs import Gdk, Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gdk, Gtk from .base import SimpleProbe @@ -6,6 +8,9 @@ class SliderProbe(SimpleProbe): native_class = Gtk.Scale + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support sliders yet") + @property def position(self): assert self.native.get_draw_value() is False diff --git a/gtk/tests_backend/widgets/splitcontainer.py b/gtk/tests_backend/widgets/splitcontainer.py index 597a0941c1..403a26bf13 100644 --- a/gtk/tests_backend/widgets/splitcontainer.py +++ b/gtk/tests_backend/widgets/splitcontainer.py @@ -1,6 +1,8 @@ import asyncio -from toga_gtk.libs import Gtk +import pytest + +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -10,6 +12,9 @@ class SplitContainerProbe(SimpleProbe): border_size = 0 direction_change_preserves_position = False + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't split containers yet") + def move_split(self, position): self.native.set_position(position) diff --git a/gtk/tests_backend/widgets/table.py b/gtk/tests_backend/widgets/table.py index 4ab4fe5f1d..ca6f57cbef 100644 --- a/gtk/tests_backend/widgets/table.py +++ b/gtk/tests_backend/widgets/table.py @@ -1,6 +1,6 @@ import pytest -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -11,6 +11,9 @@ class TableProbe(SimpleProbe): supports_keyboard_shortcuts = False supports_widgets = False + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support tables yet") + def __init__(self, widget): super().__init__(widget) self.native_table = widget._impl.native_table diff --git a/gtk/tests_backend/widgets/textinput.py b/gtk/tests_backend/widgets/textinput.py index 988e2baaae..0c34b4514f 100644 --- a/gtk/tests_backend/widgets/textinput.py +++ b/gtk/tests_backend/widgets/textinput.py @@ -1,7 +1,7 @@ import pytest from toga.constants import JUSTIFY, LEFT -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe from .properties import toga_x_text_align @@ -10,6 +10,9 @@ class TextInputProbe(SimpleProbe): native_class = Gtk.Entry + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support text input yet") + @property def value(self): return ( diff --git a/gtk/tests_backend/widgets/tree.py b/gtk/tests_backend/widgets/tree.py index facac73af1..7678ee4eab 100644 --- a/gtk/tests_backend/widgets/tree.py +++ b/gtk/tests_backend/widgets/tree.py @@ -2,7 +2,7 @@ import pytest -from toga_gtk.libs import Gtk +from toga_gtk.libs import GTK_VERSION, Gtk from .base import SimpleProbe @@ -12,6 +12,9 @@ class TreeProbe(SimpleProbe): supports_keyboard_shortcuts = False supports_widgets = False + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support trees yet") + def __init__(self, widget): super().__init__(widget) self.native_tree = widget._impl.native_tree diff --git a/gtk/tests_backend/widgets/webview.py b/gtk/tests_backend/widgets/webview.py index a9f8aea021..4161e338d7 100644 --- a/gtk/tests_backend/widgets/webview.py +++ b/gtk/tests_backend/widgets/webview.py @@ -1,8 +1,9 @@ from http.cookiejar import CookieJar +import pytest from pytest import skip -from toga_gtk.libs import WebKit2 +from toga_gtk.libs import GTK_VERSION, WebKit2 from .base import SimpleProbe @@ -13,6 +14,9 @@ class WebViewProbe(SimpleProbe): javascript_supports_exception = True supports_on_load = True + if GTK_VERSION >= (4, 0, 0): + pytest.skip("GTK4 doesn't support trees yet") + def extract_cookie(self, cookie_jar, name): assert isinstance(cookie_jar, CookieJar) skip("Cookie retrieval not implemented on GTK") diff --git a/testbed/tests/app/test_dialogs.py b/testbed/tests/app/test_dialogs.py index 12566af399..afaa38e457 100644 --- a/testbed/tests/app/test_dialogs.py +++ b/testbed/tests/app/test_dialogs.py @@ -5,6 +5,7 @@ import pytest import toga +from toga_gtk.libs import GTK_VERSION TESTS_DIR = Path(__file__).parent.parent @@ -109,6 +110,8 @@ async def test_save_file_dialog( result, ): """An app-level file open dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Save dialog is not yet supported on GTK4") dialog = toga.SaveFileDialog( "Save file", suggested_filename=filename, @@ -175,6 +178,8 @@ async def test_open_file_dialog( result, ): """An app-level file open dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Open dialog is not yet supported on GTK4") dialog = toga.OpenFileDialog( "Open file", initial_directory=initial_directory, @@ -223,6 +228,8 @@ async def test_select_folder_dialog( result, ): """An app-level folder selection dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Open dialog is not yet supported on GTK4") dialog = toga.SelectFolderDialog( "Select folder", initial_directory=initial_directory, diff --git a/testbed/tests/app/test_document_app.py b/testbed/tests/app/test_document_app.py index 7992c1748b..98cb5150ad 100644 --- a/testbed/tests/app/test_document_app.py +++ b/testbed/tests/app/test_document_app.py @@ -128,6 +128,9 @@ async def test_save_document(app, app_probe): async def test_save_as_document(monkeypatch, app, app_probe, tmp_path): """A document can be saved under a new filename.""" + if not app_probe.supports_save_dialog: + pytest.xfail("This backend doesn't support save dialogs") + # A document can be opened document_path = Path(__file__).parent / "docs/example.testbed" document = app.documents.open(document_path) diff --git a/testbed/tests/test_icons.py b/testbed/tests/test_icons.py index 499fe35df9..7f84465875 100644 --- a/testbed/tests/test_icons.py +++ b/testbed/tests/test_icons.py @@ -4,9 +4,12 @@ import pytest import toga +from toga_gtk.libs import GTK_VERSION def icon_probe(app, image): + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Labels are not yet supported on GTK4") module = import_module("tests_backend.icons") return module.IconProbe(app, image) diff --git a/testbed/tests/window/test_dialogs.py b/testbed/tests/window/test_dialogs.py index 684a9796f9..5ca8a4d2a2 100644 --- a/testbed/tests/window/test_dialogs.py +++ b/testbed/tests/window/test_dialogs.py @@ -7,6 +7,7 @@ import pytest import toga +from toga_gtk.libs import GTK_VERSION TESTS_DIR = Path(__file__).parent.parent @@ -149,6 +150,8 @@ async def test_save_file_dialog( wait_for_dialog_to_close, ): """A file open dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Save dialog is not yet supported on GTK4") dialog = toga.SaveFileDialog( "Save file", suggested_filename=filename, @@ -216,6 +219,8 @@ async def test_open_file_dialog( wait_for_dialog_to_close, ): """A file open dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Open dialog is not yet supported on GTK4") dialog = toga.OpenFileDialog( "Open file", initial_directory=initial_directory, @@ -265,6 +270,8 @@ async def test_select_folder_dialog( wait_for_dialog_to_close, ): """A folder selection dialog can be displayed and acknowledged.""" + if GTK_VERSION >= (4, 0, 0): + pytest.xfail("Open dialog is not yet supported on GTK4") dialog = toga.SelectFolderDialog( "Select folder", initial_directory=initial_directory,