Skip to content

Gui/blend text #2459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions arcade/camera/camera_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,8 @@ def point_in_view(self, point: Point2) -> bool:
"""
# This is unwrapped from standard Vec2 operations,
# The construction and garbage collection of the vectors would
# increase this method's cost by ~4x
# increase this method's cost by ~4x

pos = self.position
diff = point[0] - pos[0], point[1] - pos[1]

Expand Down Expand Up @@ -569,7 +569,6 @@ def projection(self) -> Rect:

@projection.setter
def projection(self, value: Rect) -> None:

# Unpack and validate
if not value:
raise ZeroProjectionDimension((f"Projection area is 0, {value.lrbt}"))
Expand Down
2 changes: 1 addition & 1 deletion arcade/future/splash.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ArcadeSplash(View):
dark_mode: If True, the splash screen will be shown in dark mode. (Default False)
"""

def __init__(self, view: View, duration: int = 3, dark_mode: bool = False):
def __init__(self, view: View, duration: float = 1.5, dark_mode: bool = False):
super().__init__()
self._next_view = view
self._duration = duration
Expand Down
11 changes: 8 additions & 3 deletions arcade/gui/ui_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def on_draw():

_enabled = False

DEFAULT_LAYER = 0
OVERLAY_LAYER = 10

def __init__(self, window: Optional[arcade.Window] = None):
Expand All @@ -103,7 +104,7 @@ def __init__(self, window: Optional[arcade.Window] = None):

self.register_event_type("on_event")

def add(self, widget: W, *, index=None, layer=0) -> W:
def add(self, widget: W, *, index=None, layer=DEFAULT_LAYER) -> W:
"""Add a widget to the :class:`UIManager`.

Added widgets will receive ui events and be rendered.
Expand Down Expand Up @@ -141,7 +142,9 @@ def remove(self, child: UIWidget):
child.parent = None
self.trigger_render()

def walk_widgets(self, *, root: Optional[UIWidget] = None, layer=0) -> Iterable[UIWidget]:
def walk_widgets(
self, *, root: Optional[UIWidget] = None, layer=DEFAULT_LAYER
) -> Iterable[UIWidget]:
"""Walks through widget tree, in reverse draw order (most top drawn widget first)

Args:
Expand All @@ -165,7 +168,9 @@ def clear(self):
for widget in layer[:]:
self.remove(widget)

def get_widgets_at(self, pos: Point2, cls: type[W] = UIWidget, layer=0) -> Iterable[W]:
def get_widgets_at(
self, pos: Point2, cls: type[W] = UIWidget, layer=DEFAULT_LAYER
) -> Iterable[W]:
"""Yields all widgets containing a position, returns first top laying widgets
which is instance of cls.

Expand Down
21 changes: 15 additions & 6 deletions arcade/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from __future__ import annotations

from abc import ABC
from typing import NamedTuple, Iterable, Optional, Union, TYPE_CHECKING, TypeVar, Tuple, List, Dict
from typing import Dict, Iterable, List, NamedTuple, Optional, TYPE_CHECKING, Tuple, TypeVar, Union

from pyglet.event import EventDispatcher, EVENT_HANDLED, EVENT_UNHANDLED
from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED, EventDispatcher
from pyglet.math import Vec2
from typing_extensions import Self

import arcade
from arcade import Sprite, Texture, LBWH, Rect
from arcade import LBWH, Rect, Sprite, Texture
from arcade.color import TRANSPARENT_BLACK
from arcade.gui.events import (
UIEvent,
Expand All @@ -19,9 +19,9 @@
UIOnUpdateEvent,
)
from arcade.gui.nine_patch import NinePatchTexture
from arcade.gui.property import Property, bind, ListProperty
from arcade.gui.property import ListProperty, Property, bind
from arcade.gui.surface import Surface
from arcade.types import Color, AnchorPoint, AsFloat
from arcade.types import AnchorPoint, AsFloat, Color
from arcade.utils import copy_dunders_unimplemented

if TYPE_CHECKING:
Expand Down Expand Up @@ -73,6 +73,11 @@ class UIWidget(EventDispatcher, ABC):
_padding_right = Property(0)
_padding_bottom = Property(0)
_padding_left = Property(0)
_strong_background = Property(False)
"""If True, the background will clear the surface, even if it is not fully opaque.
This is not part of the public API and subject to change.
UILabel have a strong background if set.
"""

def __init__(
self,
Expand Down Expand Up @@ -116,6 +121,7 @@ def __init__(
bind(self, "_padding_right", self.trigger_render)
bind(self, "_padding_bottom", self.trigger_render)
bind(self, "_padding_left", self.trigger_render)
bind(self, "_strong_background", self.trigger_render)

def add(self, child: W, **kwargs) -> W:
"""Add a widget as a child.
Expand Down Expand Up @@ -253,7 +259,10 @@ def do_render_base(self, surface: Surface):

# draw background
if self._bg_color:
surface.clear(self._bg_color)
if self._bg_color.a == 255 or self._strong_background:
surface.clear(self._bg_color)
else:
arcade.draw_rect_filled(LBWH(0, 0, self.width, self.height), color=self._bg_color)
# draw background texture
if self._bg_tex:
surface.draw_texture(x=0, y=0, width=self.width, height=self.height, tex=self._bg_tex)
Expand Down
2 changes: 2 additions & 0 deletions arcade/gui/widgets/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def __init__(
multiline=multiline,
**kwargs,
)
self._strong_background = True

if adaptive_multiline:
# +1 is required to prevent line wrap
width = self._label.content_width + 1
Expand Down
60 changes: 59 additions & 1 deletion arcade/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from __future__ import annotations

from ctypes import c_int, c_ubyte
from pathlib import Path
from typing import Any, Union

Expand All @@ -18,6 +19,62 @@
__all__ = ["load_font", "Text", "create_text_sprite", "draw_text"]


class _ArcadeTextLayoutGroup(pyglet.text.layout.TextLayoutGroup):
"""Create a text layout rendering group.

Overrides pyglet blending handling to allow for additive blending.
Furthermore, it resets the blend function to the previous state.
"""

_prev_blend: bool
_prev_blend_func: tuple[int, int, int, int]

def set_state(self) -> None:
self.program.use()
self.program["scissor"] = False

pyglet.gl.glActiveTexture(pyglet.gl.GL_TEXTURE0)
pyglet.gl.glBindTexture(self.texture.target, self.texture.id)

blend = c_ubyte()
pyglet.gl.glGetBooleanv(pyglet.gl.GL_BLEND, blend)
self._prev_blend = bool(blend.value)

src_rgb = c_int()
dst_rgb = c_int()
src_alpha = c_int()
dst_alpha = c_int()
pyglet.gl.glGetIntegerv(pyglet.gl.GL_BLEND_SRC_RGB, src_rgb)
pyglet.gl.glGetIntegerv(pyglet.gl.GL_BLEND_DST_RGB, dst_rgb)
pyglet.gl.glGetIntegerv(pyglet.gl.GL_BLEND_SRC_ALPHA, src_alpha)
pyglet.gl.glGetIntegerv(pyglet.gl.GL_BLEND_DST_ALPHA, dst_alpha)

self._prev_blend_func = (src_rgb.value, dst_rgb.value, src_alpha.value, dst_alpha.value)

pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
pyglet.gl.glBlendFuncSeparate(
pyglet.gl.GL_SRC_ALPHA,
pyglet.gl.GL_ONE_MINUS_SRC_ALPHA,
pyglet.gl.GL_ONE,
pyglet.gl.GL_ONE,
)

def unset_state(self) -> None:
if not self._prev_blend:
pyglet.gl.glDisable(pyglet.gl.GL_BLEND)

pyglet.gl.glBlendFuncSeparate(
self._prev_blend_func[0],
self._prev_blend_func[1],
self._prev_blend_func[2],
self._prev_blend_func[3],
)
self.program.stop()


pyglet.text.layout.TextLayout.group_class = _ArcadeTextLayoutGroup


def load_font(path: str | Path) -> None:
"""
Load fonts in a file (usually .ttf) adding them to a global font registry.
Expand Down Expand Up @@ -253,7 +310,8 @@ def __init__(
bold=bold,
italic=italic,
multiline=multiline,
rotation=rotation, # type: ignore # pending https://github.com/pyglet/pyglet/issues/843
rotation=rotation,
# type: ignore # pending https://github.com/pyglet/pyglet/issues/843
batch=batch,
group=group,
**kwargs,
Expand Down
Loading