from __future__ import annotations
from typing import Optional
from PIL import ImageEnhance
from typing_extensions import override
from arcade import Texture
from arcade.gui.events import UIOnChangeEvent, UIOnClickEvent
from arcade.gui.property import Property, bind
from arcade.gui.surface import Surface
from arcade.gui.widgets import UIInteractiveWidget
[docs]
class UITextureToggle(UIInteractiveWidget):
"""A toggle button switching between on (True) and off (False) state.
on_texture and off_texture are required.
State dependent textures are generated by changing the brightness (hover, press)
of the provided textures or converting them to grayscale (disabled).
Args:
x: x coordinate of bottom left
y: y coordinate of bottom left
width: Width of the button.
height: Height of the button.
on_texture: Texture to show when the button is on.
off_texture: Texture to show when the button is off.
value: Initial value of the button.
size_hint: Size hint for the layout.
size_hint_min: Minimum size hint for the layout.
size_hint_max: Maximum size hint for the layout.
"""
# Experimental ui class
value = Property(False)
def __init__(
self,
*,
x: float = 0,
y: float = 0,
width: float = 100,
height: float = 50,
on_texture: Optional[Texture] = None,
off_texture: Optional[Texture] = None,
value=False,
size_hint=None,
size_hint_min=None,
size_hint_max=None,
**kwargs,
):
# Generate hover and pressed texture by changing the brightness
if on_texture is None:
raise ValueError("You have to provide a `on_texture`")
self.normal_on_tex = on_texture
enhancer = ImageEnhance.Brightness(self.normal_on_tex.image)
self.hover_on_tex = Texture(
enhancer.enhance(1.5),
name=self.normal_on_tex.cache_name + "_brighter",
)
self.pressed_on_tex = Texture(
enhancer.enhance(0.5),
name=self.normal_on_tex.cache_name + "_darker",
)
self.disabled_on_tex = Texture(
self.normal_on_tex.image.convert("LA").convert("RGBA"),
)
if off_texture is None:
raise ValueError("You have to provide a `off_texture`")
self.normal_off_tex = off_texture
enhancer = ImageEnhance.Brightness(self.normal_off_tex.image)
self.hover_off_tex = Texture(
enhancer.enhance(1.5),
name=self.normal_off_tex.cache_name + "_brighter",
)
self.pressed_off_tex = Texture(
enhancer.enhance(0.5),
name=self.normal_off_tex.cache_name + "_darker",
)
self.disabled_off_tex = Texture(
self.normal_off_tex.image.convert("LA").convert("RGBA"),
)
self.value = value
self.register_event_type("on_change")
bind(self, "value", self.trigger_render)
bind(self, "value", self._dispatch_on_change_event)
super().__init__(
x=x,
y=y,
width=width,
height=height,
size_hint=size_hint,
size_hint_min=size_hint_min,
size_hint_max=size_hint_max,
**kwargs,
)
def _dispatch_on_change_event(self):
self.dispatch_event("on_change", UIOnChangeEvent(self, not self.value, self.value))
[docs]
@override
def on_click(self, event: UIOnClickEvent):
"""Change the value of the button on click."""
self.value = not self.value
[docs]
@override
def do_render(self, surface: Surface):
"""Render the button, using texture depending on the state."""
self.prepare_render(surface)
tex = self.normal_on_tex if self.value else self.normal_off_tex
if self.disabled:
tex = self.disabled_on_tex if self.value else self.disabled_off_tex
elif self.pressed:
tex = self.pressed_on_tex if self.value else self.pressed_off_tex
elif self.hovered:
tex = self.hover_on_tex if self.value else self.hover_off_tex
surface.draw_texture(0, 0, self.content_width, self.content_height, tex)
[docs]
def on_change(self, event: UIOnChangeEvent):
"""To be implemented by the user, triggered when the cursor's value is changed.
Args:
event: Event containing the old and new value of the cursor.
"""
pass