From d7092f86faf431502a067f6b7f37321f1681c8c3 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:27:50 -0400 Subject: [PATCH 01/33] Add Self annotation to gui.rect.move --- arcade/gui/widgets/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index a7108276ca..7a0ff21827 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -52,11 +52,11 @@ class Rect(NamedTuple): width: float height: float - def move(self, dx: float = 0, dy: float = 0): + def move(self, dx: float = 0, dy: float = 0) -> Self: """Returns new Rect which is moved by dx and dy""" return Rect(self.x + dx, self.y + dy, self.width, self.height) - def collide_with_point(self, x, y): + def collide_with_point(self, x, y) -> bool: left, bottom, width, height = self return left <= x <= left + width and bottom <= y <= bottom + height From 04bf83a26655ab2ac367ce2704cf552c5786a7be Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:29:38 -0400 Subject: [PATCH 02/33] Skip dot reference in gui.Rect.moveby unpacking --- arcade/gui/widgets/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 7a0ff21827..c9a280fbf3 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -54,7 +54,8 @@ class Rect(NamedTuple): def move(self, dx: float = 0, dy: float = 0) -> Self: """Returns new Rect which is moved by dx and dy""" - return Rect(self.x + dx, self.y + dy, self.width, self.height) + x, y, width, height = self + return Rect(x + dx, y + dy, width, height) def collide_with_point(self, x, y) -> bool: left, bottom, width, height = self From 8ae84b22227469e0f8eb2cbfd1cf7cf2b8ffc371 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:42:02 -0400 Subject: [PATCH 03/33] Add return annotations on gui.Rect.properties --- arcade/gui/widgets/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index c9a280fbf3..09616a5472 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -31,7 +31,7 @@ from arcade.gui.nine_patch import NinePatchTexture from arcade.gui.property import Property, bind, ListProperty from arcade.gui.surface import Surface -from arcade.types import RGBA255, Color +from arcade.types import RGBA255, Color, Point if TYPE_CHECKING: from arcade.gui.ui_manager import UIManager @@ -80,39 +80,39 @@ def resize(self, width=None, height=None): return Rect(self.x, self.y, width, height) @property - def size(self): + def size(self) -> Tuple[float, float]: return self.width, self.height @property - def left(self): + def left(self) -> float: return self.x @property - def right(self): + def right(self) -> float: return self.x + self.width @property - def bottom(self): + def bottom(self) -> float: return self.y @property - def top(self): + def top(self) -> float: return self.y + self.height @property - def center_x(self): + def center_x(self) -> float: return self.x + self.width / 2 @property - def center_y(self): + def center_y(self) -> float: return self.y + self.height / 2 @property - def center(self): + def center(self) -> Point: return self.center_x, self.center_y @property - def position(self): + def position(self) -> Point: """Bottom left coordinates""" return self.left, self.bottom From 5991832f76784c4fe700cdcce6be79b1938d4c7b Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:43:52 -0400 Subject: [PATCH 04/33] Annotate gui.Rect.resize argument types --- arcade/gui/widgets/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 09616a5472..ea3c384f7b 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -70,7 +70,11 @@ def scale(self, scale: float) -> "Rect": int(self.height * scale), ) - def resize(self, width=None, height=None): + def resize( + self, + width: float | None = None, + height: float | None = None + ) -> "Rect": """ Returns a rect with changed width and height. Fix x and y coordinate. From 4bc9d556c14c56fb2c34fb7e04929fc39c8d4e66 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:48:51 -0400 Subject: [PATCH 05/33] Add gui.Rect.resize docstring annotations --- arcade/gui/widgets/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index ea3c384f7b..478420a47f 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -75,9 +75,11 @@ def resize( width: float | None = None, height: float | None = None ) -> "Rect": - """ - Returns a rect with changed width and height. + """Return a rect with a new width or height but same lower left. + Fix x and y coordinate. + :param width: A width for the new rectangle in screen pixels. + :param height: A height for the new rectangle in screen pixels. """ width = width if width is not None else self.width height = height if height is not None else self.height From e7dade949f1316574f2dd00a13188e95d14c4a72 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:52:44 -0400 Subject: [PATCH 06/33] Document gui.Rect.size --- arcade/gui/widgets/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 478420a47f..fe5e55f5b4 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -87,6 +87,12 @@ def resize( @property def size(self) -> Tuple[float, float]: + """Read-only pixel size of the rect. + + Since these rects are immutable, use helper instance methods to + get updated rects. For example, :py:meth:`.resize` may be what + you're looking for. + """ return self.width, self.height @property From 6555bac91ca86703a2cdcfdc5c45b1753d641062 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:53:14 -0400 Subject: [PATCH 07/33] Revert mention of pixels in gui.Rect.resize --- arcade/gui/widgets/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index fe5e55f5b4..a5c8b92380 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -78,8 +78,8 @@ def resize( """Return a rect with a new width or height but same lower left. Fix x and y coordinate. - :param width: A width for the new rectangle in screen pixels. - :param height: A height for the new rectangle in screen pixels. + :param width: A width for the new rectangle. + :param height: A height for the new rectangle. """ width = width if width is not None else self.width height = height if height is not None else self.height From 44aeae70b3cd56aac27dd0a74170aec7ab9383e2 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:55:52 -0400 Subject: [PATCH 08/33] Add docstrings for edge properties with mention of axis alignment --- arcade/gui/widgets/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index a5c8b92380..8352ddd0ff 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -97,18 +97,22 @@ def size(self) -> Tuple[float, float]: @property def left(self) -> float: + """The left edge on the X axis.""" return self.x @property def right(self) -> float: + """The right edge on the X axis.""" return self.x + self.width @property def bottom(self) -> float: + """The bottom edge on the Y axis.""" return self.y @property def top(self) -> float: + """The top edge on the Y axis.""" return self.y + self.height @property From 234d968b855f4315950e5f59d456bf87f382aba4 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 14:57:26 -0400 Subject: [PATCH 09/33] Use float values to make type checkers happy --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 8352ddd0ff..574079a7c4 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -52,7 +52,7 @@ class Rect(NamedTuple): width: float height: float - def move(self, dx: float = 0, dy: float = 0) -> Self: + def move(self, dx: float = 0.0, dy: float = 0.0) -> Self: """Returns new Rect which is moved by dx and dy""" x, y, width, height = self return Rect(x + dx, y + dy, width, height) From 148d90eb5f8129cf3e7d4e88d58e4a6625735700 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:02:19 -0400 Subject: [PATCH 10/33] Add arcade.types.AsFloat alias --- arcade/types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arcade/types.py b/arcade/types.py index 5d9de3da0d..36f2dc8db0 100644 --- a/arcade/types.py +++ b/arcade/types.py @@ -34,6 +34,11 @@ if TYPE_CHECKING: from arcade.texture import Texture + +#: 1. Makes pyright happier while also telling readers +#: 2. Tells readers we're converting any ints to floats +AsFloat = Union[float, int] + MAX_UINT24 = 0xFFFFFF MAX_UINT32 = 0xFFFFFFFF From ad97d086cddeda11ec0c0c1bb5757f6dbc060357 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:03:08 -0400 Subject: [PATCH 11/33] Use AsFloat in gui.Rect.move signature --- arcade/gui/widgets/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 574079a7c4..185ccf7b5a 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -31,7 +31,7 @@ from arcade.gui.nine_patch import NinePatchTexture from arcade.gui.property import Property, bind, ListProperty from arcade.gui.surface import Surface -from arcade.types import RGBA255, Color, Point +from arcade.types import RGBA255, Color, Point, AsFloat if TYPE_CHECKING: from arcade.gui.ui_manager import UIManager @@ -52,7 +52,7 @@ class Rect(NamedTuple): width: float height: float - def move(self, dx: float = 0.0, dy: float = 0.0) -> Self: + def move(self, dx: AsFloat = 0.0, dy: AsFloat = 0.0) -> Self: """Returns new Rect which is moved by dx and dy""" x, y, width, height = self return Rect(x + dx, y + dy, width, height) From 6b180a42a8e1c2398b79fba5f962c31a887b3a10 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:03:51 -0400 Subject: [PATCH 12/33] Annotate gui.Rect.collide_with_point --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 185ccf7b5a..a31d155ef3 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -57,7 +57,7 @@ def move(self, dx: AsFloat = 0.0, dy: AsFloat = 0.0) -> Self: x, y, width, height = self return Rect(x + dx, y + dy, width, height) - def collide_with_point(self, x, y) -> bool: + def collide_with_point(self, x: AsFloat, y: AsFloat) -> bool: left, bottom, width, height = self return left <= x <= left + width and bottom <= y <= bottom + height From f4f0f2d87151ca16765fa96f607e28577761a1ee Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:08:11 -0400 Subject: [PATCH 13/33] Add docstring for gui.Rect.collide_with_point --- arcade/gui/widgets/__init__.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index a31d155ef3..c71cf4fe07 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -58,6 +58,23 @@ def move(self, dx: AsFloat = 0.0, dy: AsFloat = 0.0) -> Self: return Rect(x + dx, y + dy, width, height) def collide_with_point(self, x: AsFloat, y: AsFloat) -> bool: + """Return true if ``x`` and ``y`` are within this rect. + + This check is inclusive. Values on the :py:attr:`.left`, + :py:attr:`.right`, :py:attr:`.top`, and :py:attr:`.bottom` + edges will be counted as inside the rect. + + .. code-block:: python + + >>> bounds = Rect(0.0, 0.0, 5.0, 5.0) + >>> bounds.collide_with_point(0.0, 0.0) + True + >>> bounds.collide_with_point(5.0, 5.0) + True + + :param x: The x value to check as inside the rect. + :param y: The y value to check as inside the rect. + """ left, bottom, width, height = self return left <= x <= left + width and bottom <= y <= bottom + height From dedc2f84cdad5e22063845c198bbfc484e2ad75f Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:09:22 -0400 Subject: [PATCH 14/33] Expand docstring for gui.Rect.scale --- arcade/gui/widgets/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index c71cf4fe07..375476947d 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -79,7 +79,10 @@ def collide_with_point(self, x: AsFloat, y: AsFloat) -> bool: return left <= x <= left + width and bottom <= y <= bottom + height def scale(self, scale: float) -> "Rect": - """Returns a new rect with scale applied""" + """Return a new rect scaled relative to the origin. + + :param scale: A scale factor. + """ return Rect( int(self.x * scale), int(self.y * scale), From 2f3038bce2b192c54f50e8727daefcf63375004a Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:24:40 -0400 Subject: [PATCH 15/33] Make gui.Rect.scale's rounding adjustable --- arcade/gui/widgets/__init__.py | 36 ++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 375476947d..75a175d078 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABC +from math import floor from random import randint from typing import ( NamedTuple, @@ -12,10 +13,11 @@ Tuple, List, Dict, + Callable ) from pyglet.event import EventDispatcher, EVENT_HANDLED, EVENT_UNHANDLED -from typing_extensions import Self +from typing_extensions import Self, ParamSpec import arcade from arcade import Sprite, get_window, Texture @@ -78,16 +80,38 @@ def collide_with_point(self, x: AsFloat, y: AsFloat) -> bool: left, bottom, width, height = self return left <= x <= left + width and bottom <= y <= bottom + height - def scale(self, scale: float) -> "Rect": + def scale( + self, + scale: float, + rounding: Optional[Callable[ParamSpec, float]] = floor + ) -> "Rect": """Return a new rect scaled relative to the origin. + By default, the new rect's values are rounded down to whole + values. You can alter this by passing a different rounding + behavior: + + * Pass ``None`` to skip rounding + * Pass a function which takes a number and returns a float + to choose rounding behavior. + :param scale: A scale factor. + :param rounding: ``None`` or a callable specifying how to + round the scaled values. """ + x, y, width, height = self + if rounding is not None: + return Rect( + rounding(x * scale), + rounding(y * scale), + rounding(width * scale), + rounding(height * scale), + ) return Rect( - int(self.x * scale), - int(self.y * scale), - int(self.width * scale), - int(self.height * scale), + x * scale, + y * scale, + width * scale, + height * scale, ) def resize( From 3286ff238ffe1dbba06ef46cfb9cf99e7abc4804 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:26:01 -0400 Subject: [PATCH 16/33] use AsFloat in gui.Rect.align_right --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 75a175d078..355cb15e22 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -191,7 +191,7 @@ def align_left(self, value: float) -> "Rect": diff_x = value - self.left return self.move(dx=diff_x) - def align_right(self, value: float) -> "Rect": + def align_right(self, value: AsFloat) -> "Rect": """Returns new Rect, which is aligned to the right""" diff_x = value - self.right return self.move(dx=diff_x) From 28eea58c02e4f2475e95af266a20529a6beb62df Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:31:01 -0400 Subject: [PATCH 17/33] Type hint gui.Rect.align_center --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 355cb15e22..d39e03a05a 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -196,7 +196,7 @@ def align_right(self, value: AsFloat) -> "Rect": diff_x = value - self.right return self.move(dx=diff_x) - def align_center(self, center_x, center_y): + def align_center(self, center_x: AsFloat, center_y: AsFloat) -> "Rect": """Returns new Rect, which is aligned to the center x and y""" diff_x = center_x - self.center_x diff_y = center_y - self.center_y From c780755be26beda505fa3566958f6eb2e8c7ae83 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:31:29 -0400 Subject: [PATCH 18/33] Use AsFloat in align_center_x and align_center_y --- arcade/gui/widgets/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index d39e03a05a..e450a2259a 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -202,12 +202,12 @@ def align_center(self, center_x: AsFloat, center_y: AsFloat) -> "Rect": diff_y = center_y - self.center_y return self.move(dx=diff_x, dy=diff_y) - def align_center_x(self, value: float) -> "Rect": + def align_center_x(self, value: AsFloat) -> "Rect": """Returns new Rect, which is aligned to the center_x""" diff_x = value - self.center_x return self.move(dx=diff_x) - def align_center_y(self, value: float) -> "Rect": + def align_center_y(self, value: AsFloat) -> "Rect": """Returns new Rect, which is aligned to the center_y""" diff_y = value - self.center_y return self.move(dy=diff_y) From 87b7d7ff656294d52ba6dc794af1ebf1a09d532f Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:32:36 -0400 Subject: [PATCH 19/33] Type hint gui.Rect.min_size --- arcade/gui/widgets/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index e450a2259a..9cc594c4a8 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -212,7 +212,11 @@ def align_center_y(self, value: AsFloat) -> "Rect": diff_y = value - self.center_y return self.move(dy=diff_y) - def min_size(self, width=None, height=None): + def min_size( + self, + width: Optional[AsFloat] = None, + height: Optional[AsFloat] = None + ) -> "Rect": """ Sets the size to at least the given min values. """ From 2cd24b3d6406f0f0c3416cd7734a7714226555dc Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:35:09 -0400 Subject: [PATCH 20/33] Type hint gui.Rect.max_size --- arcade/gui/widgets/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 9cc594c4a8..38f552806c 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -227,7 +227,11 @@ def min_size( max(height or 0.0, self.height), ) - def max_size(self, width: Optional[float] = None, height: Optional[float] = None): + def max_size( + self, + width: Optional[AsFloat] = None, + height: Optional[AsFloat] = None + ) -> "Rect": """ Limits the size to the given max values. """ From 325d5fc25e158205382330834b4677d501a51bda Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:37:03 -0400 Subject: [PATCH 21/33] Skip dot access in gui.Rect.max_size --- arcade/gui/widgets/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 38f552806c..3930c9ef17 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -235,13 +235,13 @@ def max_size( """ Limits the size to the given max values. """ - w, h = self.size - if width: - w = min(width, self.width) - if height: - h = min(height, self.height) + x, y, w, h = self + if width is not None: + w = min(width, w) + if height is not None: + h = min(height, h) - return Rect(self.x, self.y, w, h) + return Rect(x, y, w, h) def union(self, rect: "Rect"): """ From d7cc8006faa05fd5b38128690c329b9122ce0268 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:38:34 -0400 Subject: [PATCH 22/33] Add return annotation on gui.Rect.union --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 3930c9ef17..d912bd9f00 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -243,7 +243,7 @@ def max_size( return Rect(x, y, w, h) - def union(self, rect: "Rect"): + def union(self, rect: "Rect") -> "Rect": """ Returns a new Rect that is the union of this rect and another. The union is the smallest rectangle that contains theses two rectangles. From 938198192d62dc02ff772ceed1ff8b5352551d81 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:49:59 -0400 Subject: [PATCH 23/33] Add tests for gui.rect.Scale --- tests/unit/gui/test_rect.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/unit/gui/test_rect.py b/tests/unit/gui/test_rect.py index 68ec78578b..b18646da05 100644 --- a/tests/unit/gui/test_rect.py +++ b/tests/unit/gui/test_rect.py @@ -1,3 +1,5 @@ +from math import ceil + from arcade.gui.widgets import Rect @@ -175,3 +177,30 @@ def test_collide_with_point(): assert rect.collide_with_point(50, 50) assert rect.collide_with_point(100, 100) assert not rect.collide_with_point(150, 150) + + +def test_rect_scale(): + rect = Rect(0, 0, 95, 99) + + # Default rounding rounds down + assert rect.scale(0.9) == (0,0, 85, 89) + + # Passing in a rounding technique works too + assert rect.scale(0.9, rounding=ceil) == (0, 0, 86, 90) + + # Passing in None applies no rounding + rect_100 = Rect(100,100,100,100) + rect_100_scaled = rect_100.scale(0.1234, None) + assert rect_100_scaled == (12.34, 12.34, 12.34, 12.34) + assert rect_100.x == 12.34 + assert rect_100.y == 12.34 + assert rect_100.width == 12.34 + assert rect_100.height == 12.34 + + # Passing in None via rounding keyword applies no rounding + rect_100_scaled = rect_100.scale(0.1234, rounding=None) + assert rect_100_scaled == (12.34, 12.34, 12.34, 12.34) + assert rect_100.x == 12.34 + assert rect_100.y == 12.34 + assert rect_100.width == 12.34 + assert rect_100.height == 12.34 From 81bb266197f4baebe9b61a9beffa5ea307e58eda Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:56:06 -0400 Subject: [PATCH 24/33] Leave off Self magic for now --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index d912bd9f00..11db3be92a 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -54,7 +54,7 @@ class Rect(NamedTuple): width: float height: float - def move(self, dx: AsFloat = 0.0, dy: AsFloat = 0.0) -> Self: + def move(self, dx: AsFloat = 0.0, dy: AsFloat = 0.0) -> "Rect": """Returns new Rect which is moved by dx and dy""" x, y, width, height = self return Rect(x + dx, y + dy, width, height) From e2670b1571618c3211466cba3fa81b717c7b2baf Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 15:56:37 -0400 Subject: [PATCH 25/33] Fix Callable first argument for now --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 11db3be92a..bd56788347 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -83,7 +83,7 @@ def collide_with_point(self, x: AsFloat, y: AsFloat) -> bool: def scale( self, scale: float, - rounding: Optional[Callable[ParamSpec, float]] = floor + rounding: Optional[Callable[..., float]] = floor ) -> "Rect": """Return a new rect scaled relative to the origin. From 573b39bbb65857a3f691091c30c8e1445ef336f4 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:04:51 -0400 Subject: [PATCH 26/33] Make UIDropDown's do_layout None-aware --- arcade/gui/widgets/dropdown.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arcade/gui/widgets/dropdown.py b/arcade/gui/widgets/dropdown.py index 91d42b2373..b6633825df 100644 --- a/arcade/gui/widgets/dropdown.py +++ b/arcade/gui/widgets/dropdown.py @@ -176,8 +176,13 @@ def do_layout(self): self._default_button.rect = self.rect # resize layout to contain widgets + overlay = self._overlay + rect = overlay.rect + if overlay.size_hint_min is not None: + rect = rect.resize(*overlay.size_hint_min) + self._overlay.rect = ( - self._overlay.rect.resize(*self._overlay.size_hint_min) + rect .align_top(self.bottom - 2) .align_left(self._default_button.left) ) From 23d4431b1ce82d3e280d77f2f723189c5a37e7f5 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:07:38 -0400 Subject: [PATCH 27/33] Remove unused import --- arcade/gui/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index bd56788347..4b8e83bb3d 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -17,7 +17,7 @@ ) from pyglet.event import EventDispatcher, EVENT_HANDLED, EVENT_UNHANDLED -from typing_extensions import Self, ParamSpec +from typing_extensions import Self import arcade from arcade import Sprite, get_window, Texture From 14dfe265eb361ae327db2202ada0ba6c0b4c7cc5 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:09:19 -0400 Subject: [PATCH 28/33] Make LAYOUT_OFFSET a float to implicit type convert to make pyright happy --- arcade/gui/widgets/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/text.py b/arcade/gui/widgets/text.py index 0e904c0042..33298ff880 100644 --- a/arcade/gui/widgets/text.py +++ b/arcade/gui/widgets/text.py @@ -309,7 +309,7 @@ class UIInputText(UIWidget): # Move layout one pixel into the scissor box so the caret is also shown at # position 0. - LAYOUT_OFFSET = 1 + LAYOUT_OFFSET: float = 1.0 def __init__( self, From 293b78c96a3d7acfdec278f6905d38e13a177849 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:15:21 -0400 Subject: [PATCH 29/33] Fix copy and paste issue in rect unit tests --- tests/unit/gui/test_rect.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/unit/gui/test_rect.py b/tests/unit/gui/test_rect.py index b18646da05..751ff3faef 100644 --- a/tests/unit/gui/test_rect.py +++ b/tests/unit/gui/test_rect.py @@ -192,15 +192,15 @@ def test_rect_scale(): rect_100 = Rect(100,100,100,100) rect_100_scaled = rect_100.scale(0.1234, None) assert rect_100_scaled == (12.34, 12.34, 12.34, 12.34) - assert rect_100.x == 12.34 - assert rect_100.y == 12.34 - assert rect_100.width == 12.34 - assert rect_100.height == 12.34 + assert rect_100_scaled.x == 12.34 + assert rect_100_scaled.y == 12.34 + assert rect_100_scaled.width == 12.34 + assert rect_100_scaled.height == 12.34 # Passing in None via rounding keyword applies no rounding rect_100_scaled = rect_100.scale(0.1234, rounding=None) assert rect_100_scaled == (12.34, 12.34, 12.34, 12.34) - assert rect_100.x == 12.34 - assert rect_100.y == 12.34 - assert rect_100.width == 12.34 - assert rect_100.height == 12.34 + assert rect_100_scaled.x == 12.34 + assert rect_100_scaled.y == 12.34 + assert rect_100_scaled.width == 12.34 + assert rect_100_scaled.height == 12.34 From 991d6cf7c95d2bb4e6a0272cf45b27d50cd67c6f Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:17:45 -0400 Subject: [PATCH 30/33] Add float conversion to make pyright happy --- arcade/gui/widgets/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arcade/gui/widgets/text.py b/arcade/gui/widgets/text.py index 33298ff880..02298daa82 100644 --- a/arcade/gui/widgets/text.py +++ b/arcade/gui/widgets/text.py @@ -351,7 +351,7 @@ def __init__( ) self.layout = pyglet.text.layout.IncrementalTextLayout( - self.doc, width - self.LAYOUT_OFFSET, height, multiline=multiline + self.doc, float(width - self.LAYOUT_OFFSET), float(height), multiline=multiline ) self.layout.x += self.LAYOUT_OFFSET self.caret = Caret(self.layout, color=Color.from_iterable(caret_color)) From 44b527a8208071c3bcf7ddeb20a24c84ceddf93e Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:24:57 -0400 Subject: [PATCH 31/33] Convert to int when interfacing with IncrementalTextLayout --- arcade/gui/widgets/text.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arcade/gui/widgets/text.py b/arcade/gui/widgets/text.py index 02298daa82..c22d92a849 100644 --- a/arcade/gui/widgets/text.py +++ b/arcade/gui/widgets/text.py @@ -309,7 +309,7 @@ class UIInputText(UIWidget): # Move layout one pixel into the scissor box so the caret is also shown at # position 0. - LAYOUT_OFFSET: float = 1.0 + LAYOUT_OFFSET = 1 def __init__( self, @@ -351,7 +351,7 @@ def __init__( ) self.layout = pyglet.text.layout.IncrementalTextLayout( - self.doc, float(width - self.LAYOUT_OFFSET), float(height), multiline=multiline + self.doc, int(width - self.LAYOUT_OFFSET), int(height), multiline=multiline ) self.layout.x += self.LAYOUT_OFFSET self.caret = Caret(self.layout, color=Color.from_iterable(caret_color)) @@ -431,8 +431,8 @@ def _update_layout(self): if layout_size != self.content_size: layout.begin_update() - layout.width = self.content_width - self.LAYOUT_OFFSET - layout.height = self.content_height + layout.width = int(self.content_width - self.LAYOUT_OFFSET) + layout.height = int(self.content_height) layout.end_update() @property @@ -522,8 +522,8 @@ def __init__( self.layout = pyglet.text.layout.ScrollableTextLayout( self.doc, - width=self.content_width, - height=self.content_height, + width=int(self.content_width), + height=int(self.content_height), multiline=multiline, ) From 4a9c130688070eed3058db5c75637db8d9f8206a Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:32:41 -0400 Subject: [PATCH 32/33] Round to int locally on UITextArea --- arcade/gui/widgets/text.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arcade/gui/widgets/text.py b/arcade/gui/widgets/text.py index c22d92a849..e35a95eb28 100644 --- a/arcade/gui/widgets/text.py +++ b/arcade/gui/widgets/text.py @@ -552,12 +552,14 @@ def text(self, value): def _update_layout(self): # Update Pyglet layout size layout = self.layout - layout_size = layout.width, layout.height - if layout_size != self.content_size: + # Convert from local float coords to ints to avoid jitter + # since pyglet imposes int-only coordinates as of pyglet 2.0 + content_width, content_height = map(int, self.content_size) + if content_width != layout.width or content_height != layout.height: layout.begin_update() - layout.width = self.content_width - layout.height = self.content_height + layout.width = content_width + layout.height = content_height layout.end_update() def do_render(self, surface: Surface): From 78711049168c291a133098df53806752fd993022 Mon Sep 17 00:00:00 2001 From: pushfoo <36696816+pushfoo@users.noreply.github.com> Date: Sat, 13 Apr 2024 17:19:42 -0400 Subject: [PATCH 33/33] Return floats from arcade.Text lrbt props --- arcade/text.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arcade/text.py b/arcade/text.py index 89fa7b2b14..e929296ac2 100644 --- a/arcade/text.py +++ b/arcade/text.py @@ -442,28 +442,28 @@ def content_height(self) -> int: return self._label.content_height @property - def left(self) -> int: + def left(self) -> float: """ Pixel location of the left content border. """ return self._label.left @property - def right(self) -> int: + def right(self) -> float: """ Pixel location of the right content border. """ return self._label.right @property - def top(self) -> int: + def top(self) -> float: """ Pixel location of the top content border. """ return self._label.top @property - def bottom(self) -> int: + def bottom(self) -> float: """ Pixel location of the bottom content border. """