Skip to content

Commit 79d22ff

Browse files
committed
Merge branch 'development' into pyglet-3
2 parents ccd0a3a + 6efb32c commit 79d22ff

File tree

10 files changed

+74
-18
lines changed

10 files changed

+74
-18
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page.
1111
### Breaking Changes
1212
- `arcade.future.input` package has been moved to the top level `arcade.input`
1313

14+
## Unreleased
15+
16+
- Upgraded Pillow to 12.0.0 for Python 3.14 support.
17+
- Adds a new `arcade.NoAracdeWindowError` exception type. This is raised when certain window operations are performed and there is no valid Arcade window found. Previously where this error would be raised, we raised a standard `RuntimeError`, this made it harder to properly catch and act accordingly. This new exception subclasses `RuntimeError`, so you can still catch this error the same way as before. The `arcade.get_window()` function will now raise this if there is no window.
18+
- Along with the new exception type, is a new `arcade.windows_exists()` function which will return True or False based on if there is currently an active window.
19+
- GUI
20+
- `UIManager` did not apply size hint of (0,0). Mainly an issue with `UIBoxLayout`.
21+
- Allow multiple children in `UIScrollArea`.
22+
- Fix `UIDropdown Overlay` positioning within a `UIScrollArea`.
23+
1424
## 3.3.3
1525

1626
- Support for Python 3.14

arcade/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def configure_logging(level: int | None = None):
9090
from .window_commands import start_render
9191
from .window_commands import unschedule
9292
from .window_commands import schedule_once
93+
from .window_commands import window_exists
9394

9495
from .sections import Section, SectionManager
9596

@@ -361,6 +362,7 @@ def configure_logging(level: int | None = None):
361362
"create_text_sprite",
362363
"clear_timings",
363364
"get_window",
365+
"window_exists",
364366
"get_fps",
365367
"has_line_of_sight",
366368
"load_animated_gif",

arcade/examples/procedural_caves_bsp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Binary Space Partitioning (BSP)
44
55
For more information, see:
6-
https://roguebasin.roguelikedevelopment.org/index.php?title=Basic_BSP_Dungeon_generation
6+
https://www.roguebasin.com/index.php?title=Basic_BSP_Dungeon_generation
77
https://github.com/DanaL/RLDungeonGenerator
88
99
If Python and Arcade are installed, this example can be run from the command line with:

arcade/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import TypeVar
88

99
__all__ = [
10+
"NoArcadeWindowError",
1011
"OutsideRangeError",
1112
"IntOutsideRangeError",
1213
"FloatOutsideRangeError",
@@ -23,6 +24,15 @@
2324
_CT = TypeVar("_CT") # Comparable type, ie supports the <= operator
2425

2526

27+
class NoArcadeWindowError(RuntimeError):
28+
"""No valid Arcade window exists.
29+
30+
It may be handled as a :py:class:`RuntimeError`.
31+
"""
32+
33+
...
34+
35+
2636
class OutsideRangeError(ValueError):
2737
"""
2838
Raised when a value is outside and expected range

arcade/gui/experimental/scroll_area.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,6 @@ def __init__(
239239

240240
def add(self, child: W, **kwargs) -> W:
241241
"""Add a child to the widget."""
242-
if self._children:
243-
raise ValueError("UIScrollArea can only have one child")
244-
245242
super().add(child, **kwargs)
246243
self.trigger_full_render()
247244

arcade/gui/ui_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ def _do_layout(self):
246246
# actual layout
247247
if child.size_hint:
248248
sh_x, sh_y = child.size_hint
249-
nw = surface_width * sh_x if sh_x else None
250-
nh = surface_height * sh_y if sh_y else None
249+
nw = surface_width * sh_x if sh_x is not None else None
250+
nh = surface_height * sh_y if sh_y is not None else None
251251
child.rect = child.rect.resize(nw, nh, anchor=AnchorPoint.BOTTOM_LEFT)
252252

253253
if child.size_hint_min:

arcade/gui/widgets/dropdown.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from arcade import uicolor
77
from arcade.gui import UIEvent, UIMousePressEvent
88
from arcade.gui.events import UIControllerButtonPressEvent, UIOnChangeEvent, UIOnClickEvent
9+
from arcade.gui.experimental import UIScrollArea
910
from arcade.gui.experimental.focus import UIFocusMixin
1011
from arcade.gui.ui_manager import UIManager
1112
from arcade.gui.widgets import UILayout, UIWidget
@@ -21,7 +22,7 @@ class _UIDropdownOverlay(UIFocusMixin, UIBoxLayout):
2122

2223
# TODO move also options logic to this class
2324

24-
def show(self, manager: UIManager):
25+
def show(self, manager: UIManager | UIScrollArea):
2526
manager.add(self, layer=UIManager.OVERLAY_LAYER)
2627

2728
def hide(self):
@@ -197,11 +198,19 @@ def _update_options(self):
197198
self._overlay.detect_focusable_widgets()
198199

199200
def _show_overlay(self):
200-
manager = self.get_ui_manager()
201-
if manager is None:
202-
raise Exception("UIDropdown could not find UIManager in its parents.")
203-
204-
self._overlay.show(manager)
201+
# traverse parents until UIManager or UIScrollArea is found
202+
parent = self.parent
203+
while parent is not None:
204+
if isinstance(parent, UIManager):
205+
break
206+
if isinstance(parent, UIScrollArea):
207+
break
208+
parent = parent.parent
209+
210+
if parent is None:
211+
raise Exception("UIDropdown could not find a valid parent for the overlay.")
212+
213+
self._overlay.show(parent)
205214

206215
def _on_button_click(self, _: UIOnClickEvent):
207216
self._show_overlay()

arcade/window_commands.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import pyglet
1616

17+
from arcade.exceptions import NoArcadeWindowError
1718
from arcade.types import RGBA255, Color
1819

1920
if TYPE_CHECKING:
@@ -26,6 +27,7 @@
2627
"get_display_size",
2728
"get_window",
2829
"set_window",
30+
"window_exists",
2931
"close_window",
3032
"run",
3133
"exit",
@@ -55,13 +57,19 @@ def get_display_size(screen_id: int = 0) -> tuple[int, int]:
5557

5658

5759
def get_window() -> Window:
58-
"""
59-
Return a handle to the current window.
60+
"""Return a handle to the current window.
61+
62+
If no window exists, it will raise an exception you can
63+
handle as a :py:class:`RuntimeError`. Use :py:func:`window_exists`
64+
to prevent raising an exception.
6065
61-
:return: Handle to the current window.
66+
Raises:
67+
:py:class:`~arcade.exceptions.NoArcadeWindowError` when no window exists.
6268
"""
6369
if _window is None:
64-
raise RuntimeError("No window is active. It has not been created yet, or it was closed.")
70+
raise NoArcadeWindowError(
71+
"No window is active. It has not been created yet, or it was closed."
72+
)
6573

6674
return _window
6775

@@ -77,6 +85,21 @@ def set_window(window: Window | None) -> None:
7785
_window = window
7886

7987

88+
def window_exists() -> bool:
89+
"""
90+
Returns True or False based on wether there is currently a Window.
91+
92+
Returns:
93+
Boolean for if a window exists.
94+
"""
95+
try:
96+
get_window()
97+
except NoArcadeWindowError:
98+
return False
99+
100+
return True
101+
102+
80103
def close_window() -> None:
81104
"""
82105
Closes the current window, and then runs garbage collection. The garbage collection

doc/tutorials/framebuffer/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ Then create a simple program with a frame buffer:
1616
Now, color everything that doesn't have an alpha of zero as green:
1717

1818
.. literalinclude:: step_02.py
19-
:caption: Pass-through frame buffer
19+
:caption: Green where alpha is not zero in the FBO
2020
:linenos:
2121

2222
Something about passing uniform data to the shader:
2323

2424
.. literalinclude:: step_03.py
25-
:caption: Pass-through frame buffer
25+
:caption: Passing uniform data to the shader
2626
:linenos:

tests/unit/gui/test_uimanager_layouting.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,21 @@ def test_supports_size_hint(window):
2525
widget3 = UIDummy()
2626
widget3.size_hint = (1, None)
2727

28+
widget4 = UIDummy()
29+
widget4.size_hint = (0, 0)
30+
2831
manager.add(widget1)
2932
manager.add(widget2)
3033
manager.add(widget3)
34+
manager.add(widget4)
3135

3236
with sized(window, 200, 300):
3337
manager.draw()
3438

3539
assert widget1.size == Vec2(200, 300)
3640
assert widget2.size == Vec2(100, 75)
3741
assert widget3.size == Vec2(200, 100)
42+
assert widget4.size == Vec2(0, 0)
3843

3944

4045
def test_supports_size_hint_min(window):

0 commit comments

Comments
 (0)