mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-23 15:23:46 +08:00
update toggle & list view
This commit is contained in:
@@ -8,6 +8,7 @@ from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.lib.button import gui_button, ButtonStyle
|
||||
from openpilot.system.ui.lib.toggle import Toggle, WIDTH as TOGGLE_WIDTH, HEIGHT as TOGGLE_HEIGHT
|
||||
from openpilot.system.ui.lib.widget import Widget
|
||||
from openpilot.system.ui.sunnypilot.lib.list_view import ListItemSP
|
||||
|
||||
ITEM_BASE_WIDTH = 600
|
||||
ITEM_BASE_HEIGHT = 170
|
||||
@@ -254,6 +255,10 @@ class ListItem(Widget):
|
||||
self._rect.y >= (self._parent_rect.y + self._parent_rect.height)):
|
||||
return
|
||||
|
||||
if ListItemSP._render(self, isinstance(self.action_item, ToggleAction)):
|
||||
return
|
||||
|
||||
|
||||
content_x = self._rect.x + ITEM_PADDING
|
||||
text_x = content_x
|
||||
|
||||
@@ -298,6 +303,10 @@ class ListItem(Widget):
|
||||
if not self.is_visible:
|
||||
return 0
|
||||
|
||||
sp_height = ListItemSP.get_item_height(self)
|
||||
if sp_height:
|
||||
return sp_height
|
||||
|
||||
current_description = self.get_description()
|
||||
if self.description_visible and current_description:
|
||||
if (
|
||||
@@ -328,6 +337,10 @@ class ListItem(Widget):
|
||||
return rl.Rectangle(item_rect.x + ITEM_PADDING, item_rect.y,
|
||||
item_rect.width - (ITEM_PADDING * 2), ITEM_BASE_HEIGHT)
|
||||
|
||||
action_rect = ListItemSP.get_action_item_rect(self, item_rect, isinstance(self.action_item, ToggleAction))
|
||||
if action_rect:
|
||||
return action_rect
|
||||
|
||||
right_x = item_rect.x + item_rect.width - right_width
|
||||
right_y = item_rect.y
|
||||
return rl.Rectangle(right_x, right_y, right_width, ITEM_BASE_HEIGHT)
|
||||
|
||||
@@ -1,59 +1,105 @@
|
||||
import pyray as rl
|
||||
|
||||
import openpilot.system.ui.lib.list_view as ListItem
|
||||
from openpilot.system.ui.lib.widget import Widget
|
||||
from openpilot.system.ui.lib.wrap_text import wrap_text
|
||||
from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
import openpilot.system.ui.sunnypilot.lib.styles as styles
|
||||
|
||||
style = styles.Default
|
||||
|
||||
LINE_PADDING = 40
|
||||
ITEM_BASE_HEIGHT = 170
|
||||
ITEM_PADDING = 20
|
||||
ITEM_TEXT_FONT_SIZE = 50
|
||||
ITEM_TEXT_COLOR = rl.WHITE
|
||||
ITEM_DESC_TEXT_COLOR = rl.Color(128, 128, 128, 255)
|
||||
ITEM_TEXT_COLOR = style.ITEM_TEXT_COLOR
|
||||
ITEM_DESC_TEXT_COLOR = style.ITEM_DESC_TEXT_COLOR
|
||||
ITEM_DESC_FONT_SIZE = 40
|
||||
ITEM_DESC_V_OFFSET = 140
|
||||
ICON_SIZE = 80
|
||||
ITEM_DESC_V_OFFSET = 150
|
||||
TOGGLE_WIDTH = 150
|
||||
BUTTON_WIDTH = 250
|
||||
BUTTON_HEIGHT = 100
|
||||
BUTTON_FONT_SIZE = 35
|
||||
|
||||
class ListItemSP(Widget):
|
||||
class ListItemSP:
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def get_item_height(self) -> float:
|
||||
if not self.is_visible:
|
||||
return 0
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
# Handle click on title/description area for toggling description
|
||||
if self.description and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
|
||||
mouse_pos = rl.get_mouse_position()
|
||||
total_width = self._rect.width - (2 * ITEM_PADDING) # Full width minus padding
|
||||
max_width = total_width - (2 * ITEM_PADDING)
|
||||
|
||||
text_area_width = rect.width - self.get_action_width() - ITEM_PADDING
|
||||
text_area_x = rect.x
|
||||
if isinstance(self, ListItem.ToggleItem):
|
||||
text_area_x = text_area_x + self.get_action_width() + ITEM_PADDING
|
||||
text_area = rl.Rectangle(text_area_x, rect.y, text_area_width, rect.height)
|
||||
current_description = self.get_description()
|
||||
if self.description_visible and current_description:
|
||||
if (
|
||||
not self._wrapped_description
|
||||
or current_description != self._prev_description
|
||||
or max_width != self._prev_max_width
|
||||
):
|
||||
self._prev_max_width = max_width
|
||||
self._prev_description = current_description
|
||||
|
||||
if rl.check_collision_point_rec(mouse_pos, text_area):
|
||||
self.show_desc = not self.show_desc
|
||||
wrapped_lines = wrap_text(self._font, current_description, ITEM_DESC_FONT_SIZE, max_width)
|
||||
self._wrapped_description = "\n".join(wrapped_lines)
|
||||
self._description_height = len(wrapped_lines) * ITEM_DESC_FONT_SIZE + 10
|
||||
return ITEM_BASE_HEIGHT + self._description_height - (ITEM_BASE_HEIGHT - ITEM_DESC_V_OFFSET) + ITEM_PADDING
|
||||
return ITEM_BASE_HEIGHT
|
||||
|
||||
# Render title and description
|
||||
x = rect.x + ITEM_PADDING
|
||||
def get_action_item_rect(self, item_rect: rl.Rectangle, left_action_item: bool) -> rl.Rectangle:
|
||||
action_width = self.action_item.rect.width
|
||||
if left_action_item:
|
||||
action_x = item_rect.x
|
||||
else:
|
||||
action_x = item_rect.x + item_rect.width - action_width
|
||||
action_y = item_rect.y
|
||||
return rl.Rectangle(action_x, action_y, action_width, ITEM_BASE_HEIGHT)
|
||||
|
||||
def _render(self, left_action_item: bool):
|
||||
content_x = self._rect.x + ITEM_PADDING
|
||||
text_x = content_x
|
||||
|
||||
if left_action_item:
|
||||
left_rect = rl.Rectangle(
|
||||
content_x,
|
||||
self._rect.y + (ITEM_BASE_HEIGHT - BUTTON_HEIGHT) // 2,
|
||||
TOGGLE_WIDTH,
|
||||
BUTTON_HEIGHT
|
||||
)
|
||||
text_x = left_rect.x + left_rect.width + ITEM_PADDING
|
||||
|
||||
# Draw title
|
||||
if self.title:
|
||||
text_size = measure_text_cached(self._font, self.title, ITEM_TEXT_FONT_SIZE)
|
||||
item_y = self._rect.y + (ITEM_BASE_HEIGHT - text_size.y) // 2
|
||||
rl.draw_text_ex(self._font, self.title, rl.Vector2(text_x, item_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_COLOR)
|
||||
|
||||
# Render toggle and handle callback
|
||||
if self.action_item.render(left_rect) and self.action_item.enabled:
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
else:
|
||||
if self.title:
|
||||
# Draw main text
|
||||
text_size = measure_text_cached(self._font, self.title, ITEM_TEXT_FONT_SIZE)
|
||||
item_y = self._rect.y + (ITEM_BASE_HEIGHT - text_size.y) // 2
|
||||
rl.draw_text_ex(self._font, self.title, rl.Vector2(text_x, item_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_COLOR)
|
||||
|
||||
# Draw right item if present
|
||||
if self.action_item:
|
||||
right_rect = self.get_right_item_rect(self._rect)
|
||||
right_rect.y = self._rect.y
|
||||
if self.action_item.render(right_rect) and self.action_item.enabled:
|
||||
# Right item was clicked/activated
|
||||
if self.callback:
|
||||
self.callback()
|
||||
|
||||
# Draw description if visible
|
||||
if self.show_desc and self._wrapped_description:
|
||||
rl.draw_text_ex(self._font, self._wrapped_description, (x, rect.y + ITEM_DESC_V_OFFSET),
|
||||
ITEM_DESC_FONT_SIZE, 0, ITEM_DESC_TEXT_COLOR)
|
||||
current_description = self.get_description()
|
||||
if self.description_visible and current_description and self._wrapped_description:
|
||||
rl.draw_text_ex(
|
||||
self._font,
|
||||
self._wrapped_description,
|
||||
rl.Vector2(content_x, self._rect.y + ITEM_DESC_V_OFFSET),
|
||||
ITEM_DESC_FONT_SIZE,
|
||||
0,
|
||||
ITEM_DESC_TEXT_COLOR,
|
||||
)
|
||||
|
||||
# Render action if needed
|
||||
action_width = self.get_action_width()
|
||||
if isinstance(self, ListItem.ToggleItem):
|
||||
action_rect = rl.Rectangle(rect.x + ITEM_PADDING, rect.y, action_width, ITEM_BASE_HEIGHT)
|
||||
x += action_width + ITEM_PADDING
|
||||
else:
|
||||
action_rect = rl.Rectangle(rect.x + rect.width - action_width, rect.y, action_width, ITEM_BASE_HEIGHT)
|
||||
|
||||
text_size = measure_text_cached(self._font, self.title, ITEM_TEXT_FONT_SIZE)
|
||||
title_y = rect.y + (ITEM_BASE_HEIGHT - text_size.y) // 2
|
||||
rl.draw_text_ex(self._font, self.title, (x, title_y), ITEM_TEXT_FONT_SIZE, 0, ITEM_TEXT_COLOR)
|
||||
|
||||
return action_rect
|
||||
return True
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import pyray as rl
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Default:
|
||||
# Base Colors
|
||||
ON_BG_COLOR = rl.Color(28, 101, 186, 255) # Blue
|
||||
OFF_BG_COLOR = rl.Color(39, 39, 39, 255) # Grey
|
||||
DISABLED_ON_BG_COLOR = rl.Color(37, 70, 107, 255) # Dull Blue
|
||||
DISABLED_OFF_BG_COLOR = rl.Color(39, 39, 39, 255) # Grey
|
||||
ITEM_TEXT_COLOR = rl.WHITE
|
||||
ITEM_DESC_TEXT_COLOR = rl.Color(128, 128, 128, 255)
|
||||
|
||||
# Toggle Colors
|
||||
TOGGLE_ON_COLOR = ON_BG_COLOR
|
||||
TOGGLE_OFF_COLOR = OFF_BG_COLOR
|
||||
TOGGLE_KNOB_COLOR = rl.WHITE
|
||||
TOGGLE_DISABLED_ON_COLOR = DISABLED_ON_BG_COLOR
|
||||
TOGGLE_DISABLED_OFF_COLOR = DISABLED_OFF_BG_COLOR
|
||||
TOGGLE_DISABLED_KNOB_COLOR = rl.Color(88, 88, 88, 255) # Lighter Grey
|
||||
@@ -1,12 +1,8 @@
|
||||
import pyray as rl
|
||||
import openpilot.system.ui.lib.toggle as ToggleOP
|
||||
import openpilot.system.ui.sunnypilot.lib.styles as styles
|
||||
|
||||
ON_COLOR = rl.Color(28, 101, 186, 255)
|
||||
OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255)
|
||||
KNOB_COLOR = rl.WHITE
|
||||
DISABLED_ON_COLOR = rl.Color(0x22, 0x77, 0x22, 255) # Dark green when disabled + on
|
||||
DISABLED_OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255)
|
||||
DISABLED_KNOB_COLOR = rl.Color(0x88, 0x88, 0x88, 255)
|
||||
STYLE = styles.Default
|
||||
WIDTH, HEIGHT = 130, 80
|
||||
BG_HEIGHT = 60
|
||||
|
||||
@@ -14,17 +10,17 @@ class ToggleSP:
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
|
||||
if self._enabled:
|
||||
bg_color = ToggleOP.Toggle._blend_color(self, OFF_COLOR, ON_COLOR, self._progress)
|
||||
knob_color = KNOB_COLOR
|
||||
bg_color = ToggleOP.Toggle._blend_color(self, STYLE.TOGGLE_OFF_COLOR, STYLE.TOGGLE_ON_COLOR, self._progress)
|
||||
knob_color = STYLE.TOGGLE_KNOB_COLOR
|
||||
else:
|
||||
bg_color = ToggleOP.Toggle._blend_color(self, DISABLED_OFF_COLOR, DISABLED_ON_COLOR, self._progress)
|
||||
knob_color = DISABLED_KNOB_COLOR
|
||||
bg_color = ToggleOP.Toggle._blend_color(self, STYLE.TOGGLE_DISABLED_OFF_COLOR, STYLE.TOGGLE_DISABLED_ON_COLOR, self._progress)
|
||||
knob_color = STYLE.TOGGLE_DISABLED_KNOB_COLOR
|
||||
|
||||
# Draw background
|
||||
bg_rect = rl.Rectangle(self._rect.x + 5, self._rect.y + 10, WIDTH - 10, BG_HEIGHT)
|
||||
|
||||
# Draw outline first
|
||||
outline_color = ON_COLOR
|
||||
outline_color = STYLE.TOGGLE_ON_COLOR
|
||||
if not self._enabled:
|
||||
# Use a more subtle color for disabled state
|
||||
outline_color = rl.Color(outline_color.r // 2, outline_color.g // 2, outline_color.b // 2, 255)
|
||||
@@ -65,13 +61,13 @@ class ToggleSP:
|
||||
rl.Vector2(int(start_x), int(start_y)),
|
||||
rl.Vector2(int(mid_x), int(mid_y)),
|
||||
3,
|
||||
ON_COLOR
|
||||
STYLE.TOGGLE_ON_COLOR
|
||||
)
|
||||
rl.draw_line_ex(
|
||||
rl.Vector2(int(mid_x), int(mid_y)),
|
||||
rl.Vector2(int(end_x), int(end_y)),
|
||||
3,
|
||||
ON_COLOR
|
||||
STYLE.TOGGLE_ON_COLOR
|
||||
)
|
||||
else:
|
||||
# Draw X when toggle is OFF
|
||||
@@ -82,12 +78,12 @@ class ToggleSP:
|
||||
rl.Vector2(int(knob_x - x_offset), int(knob_y - x_offset)),
|
||||
rl.Vector2(int(knob_x + x_offset), int(knob_y + x_offset)),
|
||||
3,
|
||||
OFF_COLOR
|
||||
STYLE.TOGGLE_OFF_COLOR
|
||||
)
|
||||
rl.draw_line_ex(
|
||||
rl.Vector2(int(knob_x + x_offset), int(knob_y - x_offset)),
|
||||
rl.Vector2(int(knob_x - x_offset), int(knob_y + x_offset)),
|
||||
3,
|
||||
OFF_COLOR
|
||||
STYLE.TOGGLE_OFF_COLOR
|
||||
)
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user