Files
sunnypilot/system/ui/widgets/button.py
Shane Smiskol 65e1fd299e raylib: fix full size alert text (#36379)
* stash so far

* try this

* better

* fast

* rename

* revert

* clean up

* yes

* hack to make it work for now

* actually fix

* fix
2025-10-15 22:53:37 -07:00

171 lines
6.6 KiB
Python

from collections.abc import Callable
from enum import IntEnum
import pyray as rl
from openpilot.system.ui.lib.application import FontWeight, MousePos
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.label import Label
class ButtonStyle(IntEnum):
NORMAL = 0 # Most common, neutral buttons
PRIMARY = 1 # For main actions
DANGER = 2 # For critical actions, like reboot or delete
TRANSPARENT = 3 # For buttons with transparent background and border
TRANSPARENT_WHITE_TEXT = 9 # For buttons with transparent background and border and white text
TRANSPARENT_WHITE_BORDER = 10 # For buttons with transparent background and white border and text
ACTION = 4
LIST_ACTION = 5 # For list items with action buttons
NO_EFFECT = 6
KEYBOARD = 7
FORGET_WIFI = 8
ICON_PADDING = 15
DEFAULT_BUTTON_FONT_SIZE = 60
ACTION_BUTTON_FONT_SIZE = 48
BUTTON_TEXT_COLOR = {
ButtonStyle.NORMAL: rl.Color(228, 228, 228, 255),
ButtonStyle.PRIMARY: rl.Color(228, 228, 228, 255),
ButtonStyle.DANGER: rl.Color(228, 228, 228, 255),
ButtonStyle.TRANSPARENT: rl.BLACK,
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.WHITE,
ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.Color(228, 228, 228, 255),
ButtonStyle.ACTION: rl.BLACK,
ButtonStyle.LIST_ACTION: rl.Color(228, 228, 228, 255),
ButtonStyle.NO_EFFECT: rl.Color(228, 228, 228, 255),
ButtonStyle.KEYBOARD: rl.Color(221, 221, 221, 255),
ButtonStyle.FORGET_WIFI: rl.Color(51, 51, 51, 255),
}
BUTTON_DISABLED_TEXT_COLORS = {
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.WHITE,
}
BUTTON_BACKGROUND_COLORS = {
ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255),
ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255),
ButtonStyle.DANGER: rl.Color(255, 36, 36, 255),
ButtonStyle.TRANSPARENT: rl.BLACK,
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK,
ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLACK,
ButtonStyle.ACTION: rl.Color(189, 189, 189, 255),
ButtonStyle.LIST_ACTION: rl.Color(57, 57, 57, 255),
ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255),
ButtonStyle.KEYBOARD: rl.Color(68, 68, 68, 255),
ButtonStyle.FORGET_WIFI: rl.Color(189, 189, 189, 255),
}
BUTTON_PRESSED_BACKGROUND_COLORS = {
ButtonStyle.NORMAL: rl.Color(74, 74, 74, 255),
ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255),
ButtonStyle.DANGER: rl.Color(255, 36, 36, 255),
ButtonStyle.TRANSPARENT: rl.BLACK,
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK,
ButtonStyle.TRANSPARENT_WHITE_BORDER: rl.BLANK,
ButtonStyle.ACTION: rl.Color(130, 130, 130, 255),
ButtonStyle.LIST_ACTION: rl.Color(74, 74, 74, 74),
ButtonStyle.NO_EFFECT: rl.Color(51, 51, 51, 255),
ButtonStyle.KEYBOARD: rl.Color(51, 51, 51, 255),
ButtonStyle.FORGET_WIFI: rl.Color(130, 130, 130, 255),
}
BUTTON_DISABLED_BACKGROUND_COLORS = {
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK,
}
class Button(Widget):
def __init__(self,
text: str,
click_callback: Callable[[], None] | None = None,
font_size: int = DEFAULT_BUTTON_FONT_SIZE,
font_weight: FontWeight = FontWeight.MEDIUM,
button_style: ButtonStyle = ButtonStyle.NORMAL,
border_radius: int = 10,
text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
text_padding: int = 20,
icon=None,
multi_touch: bool = False,
):
super().__init__()
self._button_style = button_style
self._border_radius = border_radius
self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style]
self._label = Label(text, font_size, font_weight, text_alignment, text_padding=text_padding,
text_color=BUTTON_TEXT_COLOR[self._button_style], icon=icon)
self._click_callback = click_callback
self._multi_touch = multi_touch
def set_text(self, text):
self._label.set_text(text)
def set_button_style(self, button_style: ButtonStyle):
self._button_style = button_style
self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style]
self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style])
def _update_state(self):
if self.enabled:
self._label.set_text_color(BUTTON_TEXT_COLOR[self._button_style])
if self.is_pressed:
self._background_color = BUTTON_PRESSED_BACKGROUND_COLORS[self._button_style]
else:
self._background_color = BUTTON_BACKGROUND_COLORS[self._button_style]
elif self._button_style != ButtonStyle.NO_EFFECT:
self._background_color = BUTTON_DISABLED_BACKGROUND_COLORS.get(self._button_style, rl.Color(51, 51, 51, 255))
self._label.set_text_color(BUTTON_DISABLED_TEXT_COLORS.get(self._button_style, rl.Color(228, 228, 228, 51)))
def _render(self, _):
roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2)
if self._button_style == ButtonStyle.TRANSPARENT_WHITE_BORDER:
rl.draw_rectangle_rounded(self._rect, roundness, 10, rl.BLACK)
rl.draw_rectangle_rounded_lines_ex(self._rect, roundness, 10, 2, rl.WHITE)
else:
rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color)
self._label.render(self._rect)
class ButtonRadio(Button):
def __init__(self,
text: str,
icon,
click_callback: Callable[[], None] | None = None,
font_size: int = DEFAULT_BUTTON_FONT_SIZE,
text_alignment: int = rl.GuiTextAlignment.TEXT_ALIGN_LEFT,
border_radius: int = 10,
text_padding: int = 20,
):
super().__init__(text, click_callback=click_callback, font_size=font_size,
border_radius=border_radius, text_padding=text_padding,
text_alignment=text_alignment)
self._text_padding = text_padding
self._icon = icon
self.selected = False
def _handle_mouse_release(self, mouse_pos: MousePos):
super()._handle_mouse_release(mouse_pos)
self.selected = not self.selected
def _update_state(self):
if self.selected:
self._background_color = BUTTON_BACKGROUND_COLORS[ButtonStyle.PRIMARY]
else:
self._background_color = BUTTON_BACKGROUND_COLORS[ButtonStyle.NORMAL]
def _render(self, _):
roundness = self._border_radius / (min(self._rect.width, self._rect.height) / 2)
rl.draw_rectangle_rounded(self._rect, roundness, 10, self._background_color)
self._label.render(self._rect)
if self._icon and self.selected:
icon_y = self._rect.y + (self._rect.height - self._icon.height) / 2
icon_x = self._rect.x + self._rect.width - self._icon.width - self._text_padding - ICON_PADDING
rl.draw_texture_v(self._icon, rl.Vector2(icon_x, icon_y), rl.WHITE if self.enabled else rl.Color(255, 255, 255, 100))