From df2d615fc7d17a984ad73a3130a6b3dc7ce2f1b6 Mon Sep 17 00:00:00 2001 From: Dean Lee Date: Thu, 20 Feb 2025 13:14:35 +0800 Subject: [PATCH] python ui: Implement styled rounded buttons with multiple states (#34603) * styled button * corner rounding in pixels --- system/ui/lib/button.py | 78 ++++++++++++++++++++++++++++++++++------- system/ui/reset.py | 5 +-- system/ui/text.py | 4 +-- 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/system/ui/lib/button.py b/system/ui/lib/button.py index c8fd4f3e99..9bcf3f7e01 100644 --- a/system/ui/lib/button.py +++ b/system/ui/lib/button.py @@ -1,16 +1,70 @@ - import pyray as rl -from openpilot.system.ui.lib.utils import GuiStyleContext +from enum import IntEnum +from openpilot.system.ui.lib.application import gui_app, FontWeight + + +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 + + +DEFAULT_BUTTON_FONT_SIZE = 60 +BUTTON_ENABLED_TEXT_COLOR = rl.Color(228, 228, 228, 255) +BUTTON_DISABLED_TEXT_COLOR = rl.Color(228, 228, 228, 51) + + +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, +} + +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, +} + + +def gui_button( + rect: rl.Rectangle, + text: str, + font_size: int = DEFAULT_BUTTON_FONT_SIZE, + font_weight: FontWeight = FontWeight.MEDIUM, + button_style: ButtonStyle = ButtonStyle.NORMAL, + is_enabled: bool = True, + border_radius: int = 10, # Corner rounding in pixels +) -> int: + result = 0 + + # Set background color based on button type + bg_color = BUTTON_BACKGROUND_COLORS[button_style] + if is_enabled and rl.check_collision_point_rec(rl.get_mouse_position(), rect): + if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): + bg_color = BUTTON_PRESSED_BACKGROUND_COLORS[button_style] + elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT): + result = 1 + + # Draw the button with rounded corners + roundness = border_radius / (min(rect.width, rect.height) / 2) + if button_style != ButtonStyle.TRANSPARENT: + rl.draw_rectangle_rounded(rect, roundness, 20, bg_color) + else: + rl.draw_rectangle_rounded_lines_ex(rect, roundness, 20, 2, rl.WHITE) -BUTTON_DEFAULT_BG_COLOR = rl.Color(51, 51, 51, 255) + font = gui_app.font(font_weight) + # Center text in the button + text_size = rl.measure_text_ex(font, text, font_size, 0) + text_pos = rl.Vector2( + rect.x + (rect.width - text_size.x) // 2, rect.y + (rect.height - text_size.y) // 2 + ) -def gui_button(rect, text, bg_color=BUTTON_DEFAULT_BG_COLOR, font_size: int = 0): - styles = [ - (rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE), - (rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(bg_color)) - ] - if font_size > 0: - styles.append((rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, font_size)) + # Draw the button text + text_color = BUTTON_ENABLED_TEXT_COLOR if is_enabled else BUTTON_DISABLED_TEXT_COLOR + rl.draw_text_ex(font, text, text_pos, font_size, 0, text_color) - with GuiStyleContext(styles): - return rl.gui_button(rect, text) + return result diff --git a/system/ui/reset.py b/system/ui/reset.py index 65665e36c0..a5a8a84b04 100755 --- a/system/ui/reset.py +++ b/system/ui/reset.py @@ -6,7 +6,7 @@ import threading from enum import IntEnum from openpilot.system.ui.lib.application import gui_app, FontWeight -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.label import gui_label NVME = "/dev/nvme0n1" @@ -73,7 +73,8 @@ class Reset: return False if self.reset_state != ResetState.FAILED: - if gui_button(rl.Rectangle(rect.x + button_width + 50, button_top, button_width, button_height), "Confirm", rl.Color(70, 91, 234, 255)): + if gui_button(rl.Rectangle(rect.x + button_width + 50, button_top, button_width, button_height), + "Confirm", button_style=ButtonStyle.PRIMARY): self.confirm() return True diff --git a/system/ui/text.py b/system/ui/text.py index 9777102252..ab8c429287 100755 --- a/system/ui/text.py +++ b/system/ui/text.py @@ -3,7 +3,7 @@ import sys import pyray as rl from openpilot.system.hardware import HARDWARE -from openpilot.system.ui.lib.button import gui_button +from openpilot.system.ui.lib.button import gui_button, ButtonStyle from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel from openpilot.system.ui.lib.application import gui_app @@ -56,7 +56,7 @@ def main(): rl.end_scissor_mode() button_bounds = rl.Rectangle(gui_app.width - MARGIN - BUTTON_SIZE.x, gui_app.height - MARGIN - BUTTON_SIZE.y, BUTTON_SIZE.x, BUTTON_SIZE.y) - if gui_button(button_bounds, "Reboot"): + if gui_button(button_bounds, "Reboot", button_style=ButtonStyle.TRANSPARENT): HARDWARE.reboot()