from LUIObject import LUIObject from LUILayouts import LUIHorizontalStretchedLayout from LUILabel import LUILabel from LUIInitialState import LUIInitialState __all__ = ["LUIButton"] class LUIButton(LUIObject): """ Simple button, containing three sprites and a label. """ def __init__(self, text="Button", template="ButtonDefault", **kwargs): """ Constructs a new button. The template controls which sprites to use. If the template is "ButtonDefault" for example, the sprites "ButtonDefault_Left", "ButtonDefault" and "ButtonDefault_Right" will be used. The sprites used when the button is pressed should be named "ButtonDefaultFocus_Left" and so on then. If an explicit width is set on the button, the button will stick to that width, otherwise it will automatically resize to fit the label """ LUIObject.__init__(self, x=0, y=0, solid=True) self._template = template self._layout = LUIHorizontalStretchedLayout( parent=self, prefix=self._template, width="100%") self._label = LUILabel(parent=self, text=text) self._label.z_offset = 1 self._label.center_vertical = True self._label.margin = 0, 20, 0, 20 self.margin.left = -1 self._hovered = False self._pressed = False self._use_custom_texture = False self._custom_texture = None self._custom_uv = None self._custom_texture_hover = None self._custom_uv_hover = None self._custom_texture_pressed = None self._custom_uv_pressed = None LUIInitialState.init(self, kwargs) def _apply_stretch_sizes(self): """Ensure internal sprites stretch to the button size.""" try: layout = getattr(self, '_layout', None) if layout is not None: if hasattr(layout, 'width'): layout.width = "100%" if hasattr(layout, 'height'): layout.height = "100%" inner = getattr(layout, '_layout', None) if inner is not None: if hasattr(inner, 'width'): inner.width = "100%" if hasattr(inner, 'height'): inner.height = "100%" for attr in ('_sprite_left', '_sprite_mid', '_sprite_right'): spr = getattr(layout, attr, None) if spr is not None: if hasattr(spr, 'height'): spr.height = "100%" if attr == '_sprite_mid' and hasattr(spr, 'width'): spr.width = "100%" except Exception: pass def _apply_custom_texture(self, state="normal"): """Apply custom texture based on state: normal/hover/pressed.""" if not self._use_custom_texture: return tex = None uv = None if state == "pressed" and self._custom_texture_pressed is not None: tex = self._custom_texture_pressed uv = self._custom_uv_pressed elif state == "hover" and self._custom_texture_hover is not None: tex = self._custom_texture_hover uv = self._custom_uv_hover else: tex = self._custom_texture uv = self._custom_uv if tex is None: return layout = getattr(self, "_layout", None) if layout is None: return for attr in ("_sprite_left", "_sprite_mid", "_sprite_right"): spr = getattr(layout, attr, None) if spr is None: continue try: if hasattr(spr, "set_texture"): spr.set_texture(tex, resize=False) if uv and hasattr(spr, "set_uv_range"): u0, v0, u1, v1 = uv spr.set_uv_range(u0, v0, u1, v1) except Exception: pass # Hide left/right caps for single-image mode try: if hasattr(layout, "_sprite_left") and layout._sprite_left is not None: layout._sprite_left.width = 0 if hasattr(layout, "_sprite_right") and layout._sprite_right is not None: layout._sprite_right.width = 0 except Exception: pass self._apply_stretch_sizes() def set_custom_textures(self, normal_tex, normal_uv=None, hover_tex=None, hover_uv=None, pressed_tex=None, pressed_uv=None): """Set custom textures for normal/hover/pressed states.""" self._use_custom_texture = True self._custom_texture = normal_tex self._custom_uv = normal_uv self._custom_texture_hover = hover_tex self._custom_uv_hover = hover_uv self._custom_texture_pressed = pressed_tex self._custom_uv_pressed = pressed_uv if self._hovered: self._apply_custom_texture("hover") else: self._apply_custom_texture("normal") def set_custom_texture(self, texture, uv=None): """Use a single texture for the button background.""" self.set_custom_textures(texture, uv, None, None, None, None) def clear_custom_texture(self): """Restore default template textures.""" self._use_custom_texture = False self._custom_texture = None self._custom_uv = None self._custom_texture_hover = None self._custom_uv_hover = None self._custom_texture_pressed = None self._custom_uv_pressed = None try: self._layout.prefix = self._template self._apply_stretch_sizes() except Exception: pass @property def text(self): """ Returns the current label text of the button """ return self._label.text @text.setter def text(self, text): """ Sets the label text of the button """ self._label.text = text def on_mousedown(self, event): """ Internal on_mousedown handler. Do not override """ self._pressed = True if self._use_custom_texture: self._apply_custom_texture("pressed") else: self._layout.prefix = self._template + "Focus" self._apply_stretch_sizes() self._label.margin.top = 1 def on_mouseup(self, event): """ Internal on_mouseup handler. Do not override """ self._pressed = False if self._use_custom_texture: if self._hovered: self._apply_custom_texture("hover") else: self._apply_custom_texture("normal") else: self._layout.prefix = self._template self._apply_stretch_sizes() self._label.margin.top = 0 def on_mouseover(self, event): """ Internal mouseover handler """ self._hovered = True if self._use_custom_texture and not self._pressed: self._apply_custom_texture("hover") def on_mouseout(self, event): """ Internal mouseout handler """ self._hovered = False self._pressed = False if self._use_custom_texture: self._apply_custom_texture("normal")