diff --git a/cocoa/src/toga_cocoa/widgets/scrollcontainer.py b/cocoa/src/toga_cocoa/widgets/scrollcontainer.py index c7571c75aa..b3ffeaad9c 100644 --- a/cocoa/src/toga_cocoa/widgets/scrollcontainer.py +++ b/cocoa/src/toga_cocoa/widgets/scrollcontainer.py @@ -120,23 +120,44 @@ def rehint(self): self.interface.intrinsic.height = at_least(self.interface._MIN_HEIGHT) def get_max_vertical_position(self): + print( + "document frame: " + f"{self.native.documentView.frame.size.width}x{self.native.documentView.frame.size.height}" + f"@{self.native.documentView.frame.origin.x},{self.native.documentView.frame.origin.y}" + ) + print( + "document bounds: " + f"{self.native.documentView.bounds.size.width}x{self.native.documentView.bounds.size.height}" + f"@{self.native.documentView.bounds.origin.x},{self.native.documentView.bounds.origin.y}" + ) + print( + "content frame: " + f"{self.native.contentView.frame.size.width}x{self.native.contentView.frame.size.height}" + f"@{self.native.contentView.frame.origin.x},{self.native.contentView.frame.origin.y}" + ) + print( + "content bounds: " + f"{self.native.contentView.bounds.size.width}x{self.native.contentView.bounds.size.height}" + f"@{self.native.contentView.bounds.origin.x},{self.native.contentView.bounds.origin.y}" + ) + print( + "frame: " + f"{self.native.frame.size.width}x{self.native.frame.size.height}" + f"@{self.native.frame.origin.x},{self.native.frame.origin.y}" + ) + return max( 0, - self.native.documentView.bounds.size.height - - self.native.contentView.bounds.size.height, + int( + self.native.documentView.bounds.size.height + - self.native.frame.size.height + ), ) def get_vertical_position(self): - return self.native.contentView.bounds.origin.y + return int(self.native.contentView.bounds.origin.y) def set_vertical_position(self, vertical_position): - if vertical_position < 0: - vertical_position = 0 - else: - max_value = self.get_max_vertical_position() - if vertical_position > max_value: - vertical_position = max_value - new_position = NSMakePoint( self.native.contentView.bounds.origin.x, vertical_position, @@ -146,23 +167,44 @@ def set_vertical_position(self, vertical_position): self.interface.on_scroll(None) def get_max_horizontal_position(self): + print( + "document frame: " + f"{self.native.documentView.frame.size.width}x{self.native.documentView.frame.size.height}" + f"@{self.native.documentView.frame.origin.x},{self.native.documentView.frame.origin.y}" + ) + print( + "document bounds: " + f"{self.native.documentView.bounds.size.width}x{self.native.documentView.bounds.size.height}" + f"@{self.native.documentView.bounds.origin.x},{self.native.documentView.bounds.origin.y}" + ) + print( + "content frame: " + f"{self.native.contentView.frame.size.width}x{self.native.contentView.frame.size.height}" + f"@{self.native.contentView.frame.origin.x},{self.native.contentView.frame.origin.y}" + ) + print( + "content bounds: " + f"{self.native.contentView.bounds.size.width}x{self.native.contentView.bounds.size.height}" + f"@{self.native.contentView.bounds.origin.x},{self.native.contentView.bounds.origin.y}" + ) + print( + "frame: " + f"{self.native.frame.size.width}x{self.native.frame.size.height}" + f"@{self.native.frame.origin.x},{self.native.frame.origin.y}" + ) + return max( 0, - self.native.documentView.bounds.size.width - - self.native.contentView.bounds.size.width, + int( + self.native.documentView.bounds.size.width + - self.native.frame.size.width + ), ) def get_horizontal_position(self): - return self.native.contentView.bounds.origin.x + return int(self.native.contentView.bounds.origin.x) def set_horizontal_position(self, horizontal_position): - if horizontal_position < 0: - horizontal_position = 0 - else: - max_value = self.get_max_horizontal_position() - if horizontal_position > max_value: - horizontal_position = max_value - new_position = NSMakePoint( horizontal_position, self.native.contentView.bounds.origin.y, diff --git a/core/src/toga/widgets/scrollcontainer.py b/core/src/toga/widgets/scrollcontainer.py index 7c4f89d2a8..d938264aa3 100644 --- a/core/src/toga/widgets/scrollcontainer.py +++ b/core/src/toga/widgets/scrollcontainer.py @@ -158,7 +158,16 @@ def horizontal_position(self, horizontal_position): raise ValueError( "Cannot set horizontal position when horizontal is not set." ) - self._impl.set_horizontal_position(int(horizontal_position)) + + horizontal_position = int(horizontal_position) + if horizontal_position < 0: + horizontal_position = 0 + else: + max_value = self.max_horizontal_position + if horizontal_position > max_value: + horizontal_position = max_value + + self._impl.set_horizontal_position(horizontal_position) @property def max_vertical_position(self) -> int | None: @@ -189,4 +198,13 @@ def vertical_position(self) -> int | None: def vertical_position(self, vertical_position): if not self.vertical: raise ValueError("Cannot set vertical position when vertical is not set.") - self._impl.set_vertical_position(int(vertical_position)) + + vertical_position = int(vertical_position) + if vertical_position < 0: + vertical_position = 0 + else: + max_value = self.max_vertical_position + if vertical_position > max_value: + vertical_position = max_value + + self._impl.set_vertical_position(vertical_position) diff --git a/core/tests/widgets/test_scrollcontainer.py b/core/tests/widgets/test_scrollcontainer.py index 8b392fca90..d9b1b3699c 100644 --- a/core/tests/widgets/test_scrollcontainer.py +++ b/core/tests/widgets/test_scrollcontainer.py @@ -266,11 +266,21 @@ def test_vertical(scroll_container, content, value, expected): assert_action_performed(content, "refresh") -def test_horizontal_position(scroll_container): - "The horizontal position can be set and retrieved" - scroll_container.horizontal_position = 10 +@pytest.mark.parametrize( + "position, expected", + [ + (10, 10), + (-100, 0), # Clipped to minimum value + (1500, 1000), # Clipped to maximum value + ("10", 10), # String, converted to int + (10.1, 10), # Float, converted to int + ], +) +def test_horizontal_position(scroll_container, position, expected): + "The horizontal position can be set (clipped if necessary) and retrieved" + scroll_container.horizontal_position = position - assert scroll_container.horizontal_position == 10 + assert scroll_container.horizontal_position == expected assert scroll_container.max_horizontal_position == 1000 @@ -292,11 +302,21 @@ def test_horizontal_position_when_not_horizontal(scroll_container): scroll_container.horizontal_position = 0.5 -def test_vertical_position(scroll_container): - "The vertical position can be set and retrieved" - scroll_container.vertical_position = 10 +@pytest.mark.parametrize( + "position, expected", + [ + (10, 10), + (-100, 0), # Clipped to minimum value + (2500, 2000), # Clipped to maximum value + ("10", 10), # String, converted to int + (10.1, 10), # Float, converted to int + ], +) +def test_vertical_position(scroll_container, position, expected): + "The vertical position can be set (clipped if necessary) and retrieved" + scroll_container.vertical_position = position - assert scroll_container.vertical_position == 10 + assert scroll_container.vertical_position == expected assert scroll_container.max_vertical_position == 2000 diff --git a/gtk/src/toga_gtk/container.py b/gtk/src/toga_gtk/container.py index d4ab4130a5..ab0184f014 100644 --- a/gtk/src/toga_gtk/container.py +++ b/gtk/src/toga_gtk/container.py @@ -26,6 +26,9 @@ def __init__(self): # A flag that can be used to explicitly flag that a redraw is required. self.needs_redraw = True + def refreshed(self): + pass + def make_dirty(self, widget=None): """Mark the container (or a specific widget in the container) as dirty. diff --git a/winforms/src/toga_winforms/window.py b/winforms/src/toga_winforms/window.py index c2f02ffcb5..9cbdf5e183 100644 --- a/winforms/src/toga_winforms/window.py +++ b/winforms/src/toga_winforms/window.py @@ -31,6 +31,9 @@ def dpi(self): return self.baseline_dpi return self.native.CreateGraphics().DpiX + def refreshed(self): + pass + class Window: def __init__(self, interface, title, position, size):