Source code for arcade.gui.widgets.buttons

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional, Union

from typing_extensions import TypeAlias

import arcade
from arcade import Texture, color, uicolor
from arcade.gui.nine_patch import NinePatchTexture
from arcade.gui.property import DictProperty, bind
from arcade.gui.style import UIStyleBase, UIStyledWidget
from arcade.gui.surface import Surface
from arcade.gui.widgets import UIInteractiveWidget
from arcade.gui.widgets.text import UITextWidget
from arcade.text import FontNameOrNames
from arcade.types import RGBA255


[docs] @dataclass class UITextureButtonStyle(UIStyleBase): """Used to style the texture button. Below is its use case. .. code:: py button = UITextureButton(style={"normal": UITextureButton.UIStyle(...),}) """ font_size: int = 12 font_name: FontNameOrNames = ("Kenney Future", "arial", "calibri") font_color: RGBA255 = uicolor.WHITE
[docs] class UITextureButton(UIInteractiveWidget, UIStyledWidget[UITextureButtonStyle], UITextWidget): """A button with an image for the face of the button. There are four states of the UITextureButton i.e.normal, hovered, pressed and disabled. Args: x: x coordinate of bottom left y: y coordinate of bottom left width: width of widget. Defaults to texture width if not specified. height: height of widget. Defaults to texture height if not specified. texture: texture to display for the widget. texture_hovered: different texture to display if mouse is hovering over button. texture_pressed: different texture to display if mouse button is pressed while hovering over button. text: text to add to the button. multiline: allows to wrap text, if not enough width available style: Used to style the button for different states. scale: scale the button, based on the base texture size. size_hint: Tuple of floats (0.0-1.0), how much space of the parent should be requested size_hint_min: min width and height in pixel size_hint_max: max width and height in pixel """ _textures = DictProperty[str, Union[Texture, NinePatchTexture]]() UIStyle = UITextureButtonStyle DEFAULT_STYLE = { "normal": UIStyle(), "hover": UIStyle( font_color=uicolor.WHITE_CLOUDS, ), "press": UIStyle( font_color=uicolor.DARK_BLUE_MIDNIGHT_BLUE, ), "disabled": UIStyle( font_color=uicolor.WHITE_SILVER, ), } def __init__( self, *, x: float = 0, y: float = 0, width: Optional[float] = None, height: Optional[float] = None, texture: Union[None, Texture, NinePatchTexture] = None, texture_hovered: Union[None, Texture, NinePatchTexture] = None, texture_pressed: Union[None, Texture, NinePatchTexture] = None, texture_disabled: Union[None, Texture, NinePatchTexture] = None, text: str = "", multiline: bool = False, scale: Optional[float] = None, style: Optional[dict[str, UIStyleBase]] = None, size_hint=None, size_hint_min=None, size_hint_max=None, **kwargs, ): if width is None and texture is not None: width = texture.size[0] if height is None and texture is not None: height = texture.size[1] if width is None: raise ValueError("Unable to determine a width.") if height is None: raise ValueError("Unable to determine a height.") if scale is not None and texture is not None: width = texture.size[0] * scale height = texture.size[1] * scale super().__init__( x=x, y=y, width=width, height=height, style=style or self.DEFAULT_STYLE, size_hint=size_hint, size_hint_min=size_hint_min, size_hint_max=size_hint_max, text=text, multiline=multiline, **kwargs, ) self._textures = {} if texture: self._textures["normal"] = texture self._textures["hover"] = texture self._textures["press"] = texture self._textures["disabled"] = texture if texture_hovered: self._textures["hover"] = texture_hovered if texture_pressed: self._textures["press"] = texture_pressed if texture_disabled: self._textures["disabled"] = texture_disabled bind(self, "_textures", self.trigger_render)
[docs] def get_current_state(self) -> str: """Returns the current state of the button i.e.disabled, press, hover or normal.""" if self.disabled: return "disabled" elif self.pressed: return "press" elif self.hovered: return "hover" else: return "normal"
@property def texture(self): """Returns the normal texture for the face of the button.""" return self._textures["normal"] @texture.setter def texture(self, value: Texture): self._textures["normal"] = value self.trigger_render() @property def texture_hovered(self): """Returns the hover texture for the face of the button.""" return self._textures["hover"] @texture_hovered.setter def texture_hovered(self, value: Texture): self._textures["hover"] = value self.trigger_render() @property def texture_pressed(self): """Returns the pressed texture for the face of the button.""" return self._textures["press"] @texture_pressed.setter def texture_pressed(self, value: Texture): self._textures["press"] = value self.trigger_render()
[docs] def do_render(self, surface: Surface): """Render the widgets graphical representation.""" self.prepare_render(surface) style = self.get_current_style() # update label if style is None: raise ValueError(f"No style found for state {self.get_current_state()}") self._apply_style(style) current_state = self.get_current_state() current_texture = self._textures.get(current_state) if current_texture: surface.draw_texture(0, 0, self.content_width, self.content_height, current_texture)
def _apply_style(self, style: UITextureButtonStyle): """Callback which is called right before rendering to apply changes for rendering.""" font_name = style.get("font_name", UIFlatButton.UIStyle.font_name) font_size = style.get("font_size", UIFlatButton.UIStyle.font_size) font_color = style.get("font_color", UIFlatButton.UIStyle.font_color) self.ui_label.update_font(font_name, font_size, font_color) self.ui_label.rect = self.ui_label.rect.max_size(self.content_width, self.content_height)
[docs] @dataclass class UIFlatButtonStyle(UIStyleBase): """Used to style the button. Below is its use case. .. code:: py button = UIFlatButton(style={"normal": UIFlatButton.UIStyle(...),}) """ font_size: int = 12 font_name: FontNameOrNames = ("Kenney Future", "arial", "calibri") font_color: RGBA255 = color.WHITE bg: RGBA255 = uicolor.DARK_BLUE_MIDNIGHT_BLUE border: Optional[RGBA255] = None border_width: int = 0
[docs] class UIFlatButton(UIInteractiveWidget, UIStyledWidget[UIFlatButtonStyle], UITextWidget): """A text button, with support for background color and a border. There are four states of the UITextureButton i.e. normal, hovered, pressed and disabled. Args: x: x coordinate of bottom left y: y coordinate of bottom left width: width of widget. Defaults to texture width if not specified. height: height of widget. Defaults to texture height if not specified. text: text to add to the button. multiline: allows to wrap text, if not enough width available style: Used to style the button """ UIStyle: TypeAlias = UIFlatButtonStyle DEFAULT_STYLE = { "normal": UIStyle(), "hover": UIStyle( font_color=color.WHITE, bg=uicolor.DARK_BLUE_WET_ASPHALT, border=uicolor.GRAY_CONCRETE, ), "press": UIStyle( font_color=uicolor.DARK_BLUE_MIDNIGHT_BLUE, bg=uicolor.WHITE_CLOUDS, border=uicolor.GRAY_CONCRETE, ), "disabled": UIStyle( font_color=uicolor.WHITE_SILVER, bg=uicolor.GRAY_ASBESTOS, ), } STYLE_RED = { "normal": UIStyle( font_color=uicolor.WHITE_CLOUDS, bg=uicolor.RED_ALIZARIN, border=uicolor.RED_POMEGRANATE, ), "hover": UIStyle( bg=uicolor.RED_ALIZARIN, font_color=uicolor.WHITE_CLOUDS, border=uicolor.WHITE_SILVER, border_width=2, ), "press": UIStyle( bg=uicolor.RED_POMEGRANATE, font_color=uicolor.WHITE_CLOUDS, border=uicolor.WHITE_SILVER, border_width=2, ), "disabled": UIStyle( bg=uicolor.GRAY_ASBESTOS, ), } STYLE_BLUE = { "normal": UIStyle(bg=uicolor.BLUE_PETER_RIVER, font_color=uicolor.WHITE_CLOUDS), "hover": UIStyle( bg=uicolor.BLUE_BELIZE_HOLE, font_color=uicolor.WHITE_CLOUDS, border=uicolor.WHITE_SILVER, border_width=2, ), "press": UIStyle( bg=uicolor.DARK_BLUE_MIDNIGHT_BLUE, font_color=uicolor.WHITE_CLOUDS, border=uicolor.WHITE_SILVER, border_width=2, ), "disabled": UIStyle(bg=uicolor.GRAY_ASBESTOS), } def __init__( self, *, x: float = 0, y: float = 0, width: float = 100, height: float = 50, text="", multiline=False, size_hint=None, size_hint_min=None, size_hint_max=None, style=None, **kwargs, ): 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, style=style or self.DEFAULT_STYLE, text=text, multiline=multiline, **kwargs, )
[docs] def get_current_state(self) -> str: """Returns the current state of the button i.e.disabled, press, hover or normal.""" if self.disabled: return "disabled" elif self.pressed: return "press" elif self.hovered: return "hover" else: return "normal"
[docs] def do_render(self, surface: Surface): """Render a flat button, graphical representation depends on the current state.""" self.prepare_render(surface) style = self.get_current_style() if style is None: raise ValueError(f"No style found for state {self.get_current_state()}") # update label # this might trigger another render run, due to label size change self._apply_style(style) # Render button border_width = style.get("border_width", UIFlatButton.UIStyle.border_width) border_color = style.get("border", UIFlatButton.UIStyle.border) bg_color = style.get("bg", UIFlatButton.UIStyle.bg) if bg_color: surface.clear(bg_color) # render button border (which is not the widgets border) if border_color and border_width: arcade.draw_lbwh_rectangle_outline( border_width, border_width, self.content_width - 2 * border_width, self.content_height - 2 * border_width, color=border_color, border_width=border_width, )
def _apply_style(self, style: UIStyle): """Callback which is called right before rendering to apply changes for rendering.""" font_name = style.get("font_name", UIFlatButton.UIStyle.font_name) font_size = style.get("font_size", UIFlatButton.UIStyle.font_size) font_color = style.get("font_color", UIFlatButton.UIStyle.font_color) self.ui_label.update_font(font_name, font_size, font_color)