Source code for arcade.gui.widgets.buttons

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional, Dict, Union

import arcade
from arcade import Texture
from arcade.gui.nine_patch import NinePatchTexture
from arcade.gui.property import bind, DictProperty
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 = ("calibri", "arial") font_color: RGBA255 = arcade.color.WHITE border_width: int = 2
[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. :param x: x coordinate of bottom left :param y: y coordinate of bottom left :param width: width of widget. Defaults to texture width if not specified. :param height: height of widget. Defaults to texture height if not specified. :param texture: texture to display for the widget. :param texture_hovered: different texture to display if mouse is hovering over button. :param texture_pressed: different texture to display if mouse button is pressed while hovering over button. :param text: text to add to the button. :param multiline: allows to wrap text, if not enough width available :param style: Used to style the button for different states. :param scale: scale the button, based on the base texture size. :param size_hint: Tuple of floats (0.0-1.0), how much space of the parent should be requested :param size_hint_min: min width and height in pixel :param size_hint_max: max width and height in pixel """ _textures: Dict[str, Union[Texture, NinePatchTexture]] = DictProperty() # type: ignore UIStyle = UITextureButtonStyle DEFAULT_STYLE = { "normal": UIStyle(), "hover": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.WHITE, border_width=2, ), "press": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.BLACK, border_width=2, ), "disabled": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.WHITE, border_width=2, ), } 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): self.prepare_render(surface) style = self.get_current_style() # update label 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) font_name_changed = self._label.label.font_name != font_name font_size_changed = self._label.label.font_size != font_size font_color_changed = self._label.label.color != font_color if font_name_changed or font_size_changed or font_color_changed: with self._label.label: self._label.label.font_name = font_name self._label.label.font_size = font_size self._label.label.color = font_color # make label fit its content, but limit size to button size self._label.fit_content() self.ui_label.rect = self.ui_label.rect.max_size( self.content_width, self.content_height )
[docs] class UIFlatButton(UIInteractiveWidget, UIStyledWidget, 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. :param x: x coordinate of bottom left :param y: y coordinate of bottom left :param width: width of widget. Defaults to texture width if not specified. :param height: height of widget. Defaults to texture height if not specified. :param text: text to add to the button. :param multiline: allows to wrap text, if not enough width available :param style: Used to style the button """
[docs] @dataclass class UIStyle(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 = ("calibri", "arial") font_color: RGBA255 = arcade.color.WHITE bg: RGBA255 = (21, 19, 21, 255) border: Optional[RGBA255] = None border_width: int = 0
DEFAULT_STYLE = { "normal": UIStyle(), "hover": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.WHITE, bg=(21, 19, 21, 255), border=(77, 81, 87, 255), border_width=2, ), "press": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.BLACK, bg=arcade.color.WHITE, border=arcade.color.WHITE, border_width=2, ), "disabled": UIStyle( font_size=12, font_name=("calibri", "arial"), font_color=arcade.color.WHITE, bg=arcade.color.GRAY, border=None, border_width=2, ), } 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): self.prepare_render(surface) style: UIFlatButton.UIStyle = self.get_current_style() # update label 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_xywh_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) font_name_changed = self._label.label.font_name != font_name font_size_changed = self._label.label.font_size != font_size font_color_changed = self._label.label.color != font_color if font_name_changed or font_size_changed or font_color_changed: with self._label.label: self._label.label.font_name = font_name self._label.label.font_size = font_size self._label.label.color = font_color # make label fit its content, but limit size to button size self._label.fit_content() self.ui_label.rect = self.ui_label.rect.max_size( self.content_width, self.content_height )