Skip to content

Commit 5d502a8

Browse files
authored
Gui/small improvements (#2321)
* fix(gui): event propagation and further cleanup
1 parent 7d9a26d commit 5d502a8

File tree

3 files changed

+37
-34
lines changed

3 files changed

+37
-34
lines changed

arcade/gui/surface.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from contextlib import contextmanager
44
from typing import Generator, Optional
55

6+
from PIL import Image
67
from typing_extensions import Self
78

89
import arcade
@@ -251,3 +252,7 @@ def resize(self, *, size: tuple[int, int], pixel_ratio: float) -> None:
251252
self.texture = self.ctx.texture(self.size_scaled, components=4)
252253
self.fbo = self.ctx.framebuffer(color_attachments=[self.texture])
253254
self.fbo.clear()
255+
256+
def to_image(self) -> Image.Image:
257+
"""Convert the surface to an PIL image"""
258+
return self.ctx.get_framebuffer_image(self.fbo)

arcade/gui/widgets/__init__.py

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
from abc import ABC
4-
from random import randint
54
from typing import NamedTuple, Iterable, Optional, Union, TYPE_CHECKING, TypeVar, Tuple, List, Dict
65

76
from pyglet.event import EventDispatcher, EVENT_HANDLED, EVENT_UNHANDLED
@@ -22,7 +21,7 @@
2221
from arcade.gui.nine_patch import NinePatchTexture
2322
from arcade.gui.property import Property, bind, ListProperty
2423
from arcade.gui.surface import Surface
25-
from arcade.types import RGBA255, Color, AnchorPoint, AsFloat
24+
from arcade.types import Color, AnchorPoint, AsFloat
2625
from arcade.utils import copy_dunders_unimplemented
2726

2827
if TYPE_CHECKING:
@@ -389,7 +388,7 @@ def resize(self, *, width=None, height=None, anchor: Vec2 = AnchorPoint.CENTER):
389388
"""
390389
self.rect = self.rect.resize(width=width, height=height, anchor=anchor)
391390

392-
def with_border(self, *, width=2, color=arcade.color.GRAY) -> Self:
391+
def with_border(self, *, width=2, color: Color | None = arcade.color.GRAY) -> Self:
393392
"""Sets border properties
394393
395394
Args:
@@ -526,7 +525,6 @@ class UIInteractiveWidget(UIWidget):
526525
size_hint_max: max width and height in pixel
527526
interaction_buttons: defines, which mouse buttons should trigger
528527
the interaction (default: left mouse button)
529-
style: not used
530528
"""
531529

532530
# States
@@ -616,8 +614,9 @@ def on_click(self, event: UIOnClickEvent):
616614

617615

618616
class UIDummy(UIInteractiveWidget):
619-
"""Solid color widget used for testing & examples
617+
"""Solid color widget used for testing & examples.
620618
619+
Starts with a random color.
621620
It should not be subclassed for real-world usage.
622621
623622
When clicked, it does the following:
@@ -628,14 +627,13 @@ class UIDummy(UIInteractiveWidget):
628627
Args:
629628
x: x coordinate of bottom left
630629
y: y coordinate of bottom left
631-
color: fill color for the widget
632630
width: width of widget
633631
height: height of widget
634632
size_hint: Tuple of floats (0.0-1.0), how much space of the
635633
parent should be requested
636634
size_hint_min: min width and height in pixel
637635
size_hint_max: max width and height in pixel
638-
style: not used
636+
**kwargs: passed to UIWidget
639637
"""
640638

641639
def __init__(
@@ -658,25 +656,22 @@ def __init__(
658656
size_hint=size_hint,
659657
size_hint_min=size_hint_min,
660658
size_hint_max=size_hint_max,
659+
**kwargs,
661660
)
662-
self.color: RGBA255 = (randint(0, 255), randint(0, 255), randint(0, 255), 255)
663-
self.border_color = arcade.color.BATTLESHIP_GREY
664-
self.border_width = 0
661+
self.with_background(color=Color.random(a=255))
662+
self.with_border(color=arcade.color.BATTLESHIP_GREY, width=0)
665663

666664
def on_click(self, event: UIOnClickEvent):
667665
"""Prints the rect and changes the color"""
668666
print("UIDummy.rect:", self.rect)
669-
self.color = Color.random(a=255)
667+
self.with_background(color=Color.random(a=255))
670668

671669
def on_update(self, dt):
672670
"""Update the border of the widget if hovered"""
673-
self.border_width = 2 if self.hovered else 0
674-
self.border_color = arcade.color.WHITE if self.pressed else arcade.color.BATTLESHIP_GREY
675-
676-
def do_render(self, surface: Surface):
677-
"""Render solid color"""
678-
self.prepare_render(surface)
679-
surface.clear(self.color)
671+
self.with_border(
672+
width=2 if self.hovered else 0,
673+
color=arcade.color.WHITE if self.pressed else arcade.color.BATTLESHIP_GREY,
674+
)
680675

681676

682677
class UISpriteWidget(UIWidget):
@@ -692,7 +687,6 @@ class UISpriteWidget(UIWidget):
692687
parent should be requested
693688
size_hint_min: min width and height in pixel
694689
size_hint_max: max width and height in pixel
695-
style: not used
696690
"""
697691

698692
def __init__(
@@ -748,7 +742,6 @@ class UILayout(UIWidget):
748742
parent should be requested
749743
size_hint_min: min width and height in pixel
750744
size_hint_max: max width and height in pixel
751-
style: not used
752745
"""
753746

754747
@staticmethod
@@ -808,7 +801,8 @@ class UISpace(UIWidget):
808801
y: y coordinate of bottom left
809802
width: width of widget
810803
height: height of widget
811-
color: Color for widget area
804+
color: Color for widget area, if None, it will be transparent
805+
(this will set the background color)
812806
size_hint: Tuple of floats (0.0-1.0), how much space of the
813807
parent should be requested
814808
size_hint_min: min width and height in pixel
@@ -839,20 +833,13 @@ def __init__(
839833
size_hint_max=size_hint_max,
840834
**kwargs,
841835
)
842-
self._color = color
836+
self.with_background(color=color)
843837

844838
@property
845839
def color(self):
846-
"""Color of the widget"""
847-
return self._color
840+
"""Color of the widget, alias for background color"""
841+
return self._bg_color
848842

849843
@color.setter
850844
def color(self, value):
851-
self._color = value
852-
self.trigger_render()
853-
854-
def do_render(self, surface: Surface):
855-
"""Render the widget, mainly the background color"""
856-
self.prepare_render(surface)
857-
if self._color:
858-
surface.clear(self._color)
845+
self.with_background(color=value)

arcade/gui/widgets/text.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ class UIInputText(UIWidget):
362362
around the caret. Arcade confirms that the field is active before allowing
363363
users to type, so it is okay to have multiple of these.
364364
365+
By default, a border is drawn around the input field.
366+
365367
Args:
366368
x: x position (default anchor is bottom-left).
367369
y: y position (default anchor is bottom-left).
@@ -380,6 +382,9 @@ class UIInputText(UIWidget):
380382
is the same thing as a :py:class:`~arcade.gui.UITextArea`.
381383
caret_color: An RGBA or RGB color for the caret with each
382384
channel between 0 and 255, inclusive.
385+
border_color: An RGBA or RGB color for the border with each
386+
channel between 0 and 255, inclusive, can be None to remove border.
387+
border_width: Width of the border in pixels.
383388
size_hint: A tuple of floats between 0 and 1 defining the amount
384389
of space of the parent should be requested.
385390
size_hint_min: Minimum size hint width and height in pixel.
@@ -398,13 +403,15 @@ def __init__(
398403
x: float = 0,
399404
y: float = 0,
400405
width: float = 100,
401-
height: float = 24,
406+
height: float = 23, # required height for font size 12 + border width 1
402407
text: str = "",
403408
font_name=("Arial",),
404409
font_size: float = 12,
405410
text_color: RGBOrA255 = arcade.color.WHITE,
406411
multiline=False,
407412
caret_color: RGBOrA255 = arcade.color.WHITE,
413+
border_color: Color | None = arcade.color.WHITE,
414+
border_width: int = 2,
408415
size_hint=None,
409416
size_hint_min=None,
410417
size_hint_max=None,
@@ -421,6 +428,8 @@ def __init__(
421428
**kwargs,
422429
)
423430

431+
self.with_border(color=border_color, width=border_width)
432+
424433
self._active = False
425434
self._text_color = Color.from_iterable(text_color)
426435

@@ -467,7 +476,8 @@ def on_event(self, event: UIEvent) -> Optional[bool]:
467476
if not self._active and isinstance(event, UIMousePressEvent):
468477
if self.rect.point_in_rect(event.pos):
469478
self.activate()
470-
return EVENT_HANDLED
479+
# return unhandled to allow other widgets to deactivate
480+
return EVENT_UNHANDLED
471481

472482
# If active check to deactivate
473483
if self._active and isinstance(event, UIMousePressEvent):
@@ -477,6 +487,7 @@ def on_event(self, event: UIEvent) -> Optional[bool]:
477487
self.caret.on_mouse_press(x, y, event.button, event.modifiers)
478488
else:
479489
self.deactivate()
490+
# return unhandled to allow other widgets to activate
480491
return EVENT_UNHANDLED
481492

482493
# If active pass all non press events to caret

0 commit comments

Comments
 (0)