mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-29 10:53:49 +08:00
BigUI: Purple Rain
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import replace
|
||||
|
||||
import pyray as rl
|
||||
|
||||
@@ -32,8 +32,9 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.aethergrid import (
|
||||
draw_rounded_fill,
|
||||
draw_rounded_stroke,
|
||||
draw_settings_list_row,
|
||||
draw_soft_card,
|
||||
mix_colors,
|
||||
draw_text_fit_common,
|
||||
snap_rect,
|
||||
with_alpha,
|
||||
)
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
from openpilot.selfdrive.ui.lib.fingerprint_catalog import (
|
||||
@@ -443,9 +444,6 @@ class VehicleSettingsManagerView(PanelManagerView):
|
||||
|
||||
|
||||
class ButtonActionComboDialog(Widget):
|
||||
_DIALOG_W = 660
|
||||
_DIALOG_H = 440
|
||||
|
||||
def __init__(self, controller: "StarPilotVehicleSettingsLayout", title: str,
|
||||
keys: tuple[str, ...], labels: tuple[str, ...]):
|
||||
super().__init__()
|
||||
@@ -453,60 +451,54 @@ class ButtonActionComboDialog(Widget):
|
||||
self._title = title
|
||||
self._keys = keys
|
||||
self._labels = labels
|
||||
self._pressed_idx: int | None = None
|
||||
self._pressed_close = False
|
||||
self._snapshot = {key: controller._params.get_int(key) for key in keys}
|
||||
self._pressed_zone: str | None = None
|
||||
self._font_title = gui_app.font(FontWeight.BOLD)
|
||||
self._font_btn = gui_app.font(FontWeight.SEMI_BOLD)
|
||||
|
||||
def _layout(self):
|
||||
sw = gui_app.width
|
||||
sh = gui_app.height
|
||||
dx = (sw - self._DIALOG_W) / 2
|
||||
dy = (sh - self._DIALOG_H) / 2
|
||||
d_rect = rl.Rectangle(int(dx), int(dy), int(self._DIALOG_W), int(self._DIALOG_H))
|
||||
|
||||
rows_area_y = dy + 72
|
||||
rows_area_h = len(self._keys) * 88 + 12
|
||||
rows_area = rl.Rectangle(int(dx + 40), int(rows_area_y), int(self._DIALOG_W - 80), int(rows_area_h))
|
||||
|
||||
row_rects = []
|
||||
for i in range(len(self._keys)):
|
||||
row_rects.append(rl.Rectangle(
|
||||
int(dx + 40), int(rows_area_y + i * 88),
|
||||
int(self._DIALOG_W - 80), 88,
|
||||
))
|
||||
|
||||
close_w = 320
|
||||
close_h = 64
|
||||
close_rect = rl.Rectangle(
|
||||
int(dx + (self._DIALOG_W - close_w) / 2),
|
||||
int(dy + self._DIALOG_H - 36 - close_h),
|
||||
close_w, close_h,
|
||||
)
|
||||
return d_rect, rows_area, row_rects, close_rect
|
||||
self._ok_offset = 0.0
|
||||
self._cancel_offset = 0.0
|
||||
self._ok_target = 0.0
|
||||
self._cancel_target = 0.0
|
||||
self._row_rects: list[rl.Rectangle] = []
|
||||
self._cancel_rect = rl.Rectangle(0, 0, 0, 0)
|
||||
self._ok_rect = rl.Rectangle(0, 0, 0, 0)
|
||||
|
||||
def _handle_mouse_press(self, mouse_pos):
|
||||
_, _, row_rects, close_rect = self._layout()
|
||||
if rl.check_collision_point_rec(mouse_pos, close_rect):
|
||||
self._pressed_close = True
|
||||
return
|
||||
for i, rect in enumerate(row_rects):
|
||||
for i, rect in enumerate(self._row_rects):
|
||||
if rl.check_collision_point_rec(mouse_pos, rect):
|
||||
self._pressed_idx = i
|
||||
self._pressed_zone = f"row:{i}"
|
||||
return
|
||||
if rl.check_collision_point_rec(mouse_pos, self._cancel_rect):
|
||||
self._pressed_zone = "cancel"
|
||||
self._cancel_target = 1.0
|
||||
return
|
||||
if rl.check_collision_point_rec(mouse_pos, self._ok_rect):
|
||||
self._pressed_zone = "ok"
|
||||
self._ok_target = 1.0
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos):
|
||||
if self._pressed_idx is not None:
|
||||
idx = self._pressed_idx
|
||||
self._pressed_idx = None
|
||||
_, _, row_rects, _ = self._layout()
|
||||
if idx < len(row_rects) and rl.check_collision_point_rec(mouse_pos, row_rects[idx]):
|
||||
zone = self._pressed_zone
|
||||
self._pressed_zone = None
|
||||
if not zone:
|
||||
return
|
||||
|
||||
if zone.startswith("row:"):
|
||||
idx = int(zone.split(":")[1])
|
||||
if idx < len(self._row_rects) and rl.check_collision_point_rec(mouse_pos, self._row_rects[idx]):
|
||||
self._controller._show_action_picker(self._keys[idx])
|
||||
return
|
||||
if self._pressed_close:
|
||||
self._pressed_close = False
|
||||
_, _, _, close_rect = self._layout()
|
||||
if rl.check_collision_point_rec(mouse_pos, close_rect):
|
||||
|
||||
if zone == "cancel":
|
||||
self._cancel_target = 0.0
|
||||
if rl.check_collision_point_rec(mouse_pos, self._cancel_rect):
|
||||
for k, v in self._snapshot.items():
|
||||
self._controller._params.put_int(k, v)
|
||||
gui_app.pop_widget()
|
||||
return
|
||||
|
||||
if zone == "ok":
|
||||
self._ok_target = 0.0
|
||||
if rl.check_collision_point_rec(mouse_pos, self._ok_rect):
|
||||
gui_app.pop_widget()
|
||||
|
||||
def _render(self, rect):
|
||||
@@ -514,63 +506,119 @@ class ButtonActionComboDialog(Widget):
|
||||
sh = gui_app.height
|
||||
rl.draw_rectangle(0, 0, sw, sh, rl.Color(0, 0, 0, 160))
|
||||
|
||||
d_rect, rows_area, row_rects, close_rect = self._layout()
|
||||
dialog_w = min(2320, int(rect.width - 40))
|
||||
dialog_h = max(min(1015, int(rect.height - 40)), 968)
|
||||
dx = rect.x + (rect.width - dialog_w) / 2
|
||||
dy = rect.y + (rect.height - dialog_h) / 2
|
||||
d_rect = snap_rect(rl.Rectangle(int(dx), int(dy), dialog_w, dialog_h))
|
||||
|
||||
btn_y = int(dy + dialog_h - 160 - 87)
|
||||
btn_pair_w = 600 + 40 + 600
|
||||
btn_start_x = int(dx + (dialog_w - btn_pair_w) / 2)
|
||||
self._cancel_rect = rl.Rectangle(btn_start_x, btn_y, 600, 160)
|
||||
self._ok_rect = rl.Rectangle(btn_start_x + 600 + 40, btn_y, 600, 160)
|
||||
|
||||
row_margin = 80
|
||||
self._row_rects = []
|
||||
for i in range(len(self._keys)):
|
||||
self._row_rects.append(rl.Rectangle(
|
||||
int(dx + row_margin), int(dy + 130 + i * (170 + 40)),
|
||||
dialog_w - 2 * row_margin, 170,
|
||||
))
|
||||
|
||||
mouse_pos = gui_app.last_mouse_event.pos
|
||||
|
||||
draw_rounded_fill(d_rect, rl.Color(10, 12, 16, 255), radius_px=24)
|
||||
draw_rounded_stroke(d_rect, rl.Color(255, 255, 255, 16), radius_px=24)
|
||||
rl.draw_rectangle_rec(rl.Rectangle(d_rect.x, d_rect.y, d_rect.width, 4), PANEL_STYLE.accent)
|
||||
draw_rounded_fill(d_rect, rl.Color(10, 12, 16, 255), radius_px=35)
|
||||
draw_rounded_stroke(d_rect, rl.Color(255, 255, 255, 16), radius_px=35)
|
||||
rl.draw_rectangle_rec(rl.Rectangle(d_rect.x, d_rect.y, d_rect.width, 3), PANEL_STYLE.accent)
|
||||
|
||||
title_size = 32
|
||||
title_size = 64
|
||||
ts = measure_text_cached(self._font_title, self._title, title_size)
|
||||
rl.draw_text_ex(self._font_title, self._title,
|
||||
rl.Vector2(int(d_rect.x + (d_rect.width - ts.x) / 2),
|
||||
int(d_rect.y + (64 - title_size) / 2)),
|
||||
rl.Vector2(int(dx + (dialog_w - ts.x) / 2), int(dy + 87)),
|
||||
title_size, 0, rl.WHITE)
|
||||
|
||||
draw_list_group_shell(rows_area, style=PANEL_STYLE)
|
||||
font_label = gui_app.font(FontWeight.MEDIUM)
|
||||
font_value = gui_app.font(FontWeight.MEDIUM)
|
||||
value_fs = 48
|
||||
title_fs = 48
|
||||
|
||||
for i in range(len(self._keys)):
|
||||
row_rect = row_rects[i]
|
||||
row_rect = self._row_rects[i]
|
||||
hovered = rl.check_collision_point_rec(mouse_pos, row_rect)
|
||||
pressed = self._pressed_idx == i
|
||||
pressed = self._pressed_zone == f"row:{i}"
|
||||
|
||||
draw_settings_list_row(
|
||||
row_rect,
|
||||
title=self._labels[i],
|
||||
value=self._controller._get_action_name(self._keys[i]),
|
||||
enabled=True,
|
||||
hovered=hovered and (self._pressed_idx is None),
|
||||
pressed=pressed,
|
||||
is_last=(i == len(self._keys) - 1),
|
||||
show_chevron=True,
|
||||
title_size=46,
|
||||
value_size=42,
|
||||
style=PANEL_STYLE,
|
||||
)
|
||||
bg = rl.Color(255, 255, 255, 4)
|
||||
if pressed:
|
||||
bg = rl.Color(255, 255, 255, 12)
|
||||
elif hovered:
|
||||
bg = rl.Color(255, 255, 255, 8)
|
||||
draw_rounded_fill(row_rect, bg, radius_px=8)
|
||||
draw_rounded_stroke(row_rect, rl.Color(255, 255, 255, 6), radius_px=8)
|
||||
|
||||
close_hovered = rl.check_collision_point_rec(mouse_pos, close_rect)
|
||||
close_pressed = self._pressed_close
|
||||
if i < len(self._keys) - 1:
|
||||
sep_y = int(row_rect.y + row_rect.height - 1)
|
||||
rl.draw_line(int(row_rect.x + 24), sep_y, int(row_rect.x + row_rect.width - 24), sep_y, rl.Color(255, 255, 255, 16))
|
||||
|
||||
if close_pressed:
|
||||
close_fill = mix_colors(PANEL_STYLE.accent, rl.Color(0, 0, 0, 255), 0.2)
|
||||
close_border = PANEL_STYLE.accent
|
||||
elif close_hovered:
|
||||
close_fill = PANEL_STYLE.accent
|
||||
close_border = PANEL_STYLE.accent
|
||||
title_x = int(row_rect.x + 24)
|
||||
title_y = int(row_rect.y + (row_rect.height - title_fs) / 2)
|
||||
rl.draw_text_ex(self._font_title, self._labels[i], rl.Vector2(title_x, title_y), title_fs, 0, rl.WHITE)
|
||||
|
||||
action_name = self._controller._get_action_name(self._keys[i])
|
||||
value_left = int(title_x + 350)
|
||||
value_right = int(row_rect.x + row_rect.width - 24)
|
||||
available_w = value_right - value_left
|
||||
text_w = measure_text_cached(font_label, action_name, value_fs).x
|
||||
if text_w <= available_w:
|
||||
val_x = value_right - text_w
|
||||
val_y = int(row_rect.y + (row_rect.height - value_fs) / 2)
|
||||
rl.draw_text_ex(font_value, action_name, rl.Vector2(val_x, val_y), value_fs, 0, rl.WHITE)
|
||||
else:
|
||||
draw_text_fit_common(
|
||||
font_value, action_name,
|
||||
rl.Vector2(value_left, int(row_rect.y + (row_rect.height - value_fs) / 2)),
|
||||
available_w, value_fs, color=rl.WHITE,
|
||||
)
|
||||
|
||||
dt = rl.get_frame_time()
|
||||
self._ok_offset += (self._ok_target - self._ok_offset) * (1 - math.exp(-dt / 0.060))
|
||||
self._cancel_offset += (self._cancel_target - self._cancel_offset) * (1 - math.exp(-dt / 0.060))
|
||||
|
||||
c_y = self._cancel_rect.y + min(1.0, 10 * self._cancel_offset * 0.1)
|
||||
c_face = snap_rect(rl.Rectangle(self._cancel_rect.x, c_y, 600, 160))
|
||||
c_hovered = rl.check_collision_point_rec(mouse_pos, self._cancel_rect)
|
||||
c_pressed = self._pressed_zone == "cancel"
|
||||
|
||||
if c_pressed:
|
||||
c_fill = rl.Color(50, 54, 64, 255)
|
||||
c_border = rl.Color(255, 255, 255, 40)
|
||||
elif c_hovered:
|
||||
c_fill = rl.Color(40, 44, 54, 255)
|
||||
c_border = rl.Color(255, 255, 255, 30)
|
||||
else:
|
||||
close_fill = rl.Color(255, 255, 255, 14)
|
||||
close_border = rl.Color(255, 255, 255, 28)
|
||||
c_fill = rl.Color(34, 38, 48, 255)
|
||||
c_border = rl.Color(255, 255, 255, 20)
|
||||
|
||||
draw_soft_card(close_rect, close_fill, close_border)
|
||||
draw_rounded_fill(c_face, c_fill, radius_px=41)
|
||||
draw_rounded_stroke(c_face, c_border, radius_px=41)
|
||||
c_text = tr("CANCEL")
|
||||
cts = measure_text_cached(self._font_btn, c_text, 49)
|
||||
rl.draw_text_ex(self._font_btn, c_text,
|
||||
rl.Vector2(int(c_face.x + (c_face.width - cts.x) / 2),
|
||||
int(c_face.y + (c_face.height - cts.y) / 2)),
|
||||
49, 0, rl.WHITE)
|
||||
|
||||
close_text = tr("Close")
|
||||
close_size = 22
|
||||
cts = measure_text_cached(self._font_btn, close_text, close_size)
|
||||
rl.draw_text_ex(self._font_btn, close_text,
|
||||
rl.Vector2(int(close_rect.x + (close_rect.width - cts.x) / 2),
|
||||
int(close_rect.y + (close_rect.height - cts.y) / 2)),
|
||||
close_size, 0, rl.WHITE)
|
||||
o_y = self._ok_rect.y + min(1.0, 10 * self._ok_offset * 0.1)
|
||||
o_face = snap_rect(rl.Rectangle(self._ok_rect.x, o_y, 600, 160))
|
||||
|
||||
draw_rounded_fill(o_face, PANEL_STYLE.accent, radius_px=41)
|
||||
draw_rounded_stroke(o_face, with_alpha(PANEL_STYLE.accent, 150), radius_px=41)
|
||||
o_text = tr("OK")
|
||||
ots = measure_text_cached(self._font_btn, o_text, 49)
|
||||
rl.draw_text_ex(self._font_btn, o_text,
|
||||
rl.Vector2(int(o_face.x + (o_face.width - ots.x) / 2),
|
||||
int(o_face.y + (o_face.height - ots.y) / 2)),
|
||||
49, 0, rl.WHITE)
|
||||
|
||||
|
||||
class StarPilotVehicleSettingsLayout(_SettingsPage):
|
||||
|
||||
@@ -47,7 +47,7 @@ BUTTON_DISABLED_TEXT_COLORS = {
|
||||
|
||||
BUTTON_BACKGROUND_COLORS = {
|
||||
ButtonStyle.NORMAL: rl.Color(51, 51, 51, 255),
|
||||
ButtonStyle.PRIMARY: rl.Color(70, 91, 234, 255),
|
||||
ButtonStyle.PRIMARY: rl.Color(139, 92, 246, 255),
|
||||
ButtonStyle.DANGER: rl.Color(226, 44, 44, 255),
|
||||
ButtonStyle.TRANSPARENT: rl.BLACK,
|
||||
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK,
|
||||
@@ -61,7 +61,7 @@ BUTTON_BACKGROUND_COLORS = {
|
||||
|
||||
BUTTON_PRESSED_BACKGROUND_COLORS = {
|
||||
ButtonStyle.NORMAL: rl.Color(74, 74, 74, 255),
|
||||
ButtonStyle.PRIMARY: rl.Color(48, 73, 244, 255),
|
||||
ButtonStyle.PRIMARY: rl.Color(120, 78, 220, 255),
|
||||
ButtonStyle.DANGER: rl.Color(255, 36, 36, 255),
|
||||
ButtonStyle.TRANSPARENT: rl.BLACK,
|
||||
ButtonStyle.TRANSPARENT_WHITE_TEXT: rl.BLANK,
|
||||
|
||||
@@ -46,7 +46,7 @@ class MultiOptionDialog(Widget):
|
||||
|
||||
def _render(self, rect):
|
||||
dialog_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - 2 * MARGIN, rect.height - 2 * MARGIN)
|
||||
rl.draw_rectangle_rounded(dialog_rect, 0.02, 20, rl.Color(30, 30, 30, 255))
|
||||
rl.draw_rectangle_rounded(dialog_rect, 0.02, 20, rl.Color(10, 12, 16, 255))
|
||||
|
||||
content_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + MARGIN,
|
||||
dialog_rect.width - 2 * MARGIN, dialog_rect.height - 2 * MARGIN)
|
||||
|
||||
Reference in New Issue
Block a user