ui: implement change language in settings (#35481)
implement change language in settingspull/35500/head
parent
a3daca8fd5
commit
f0f249ecf8
2 changed files with 77 additions and 65 deletions
@ -1,83 +1,70 @@ |
||||
import pyray as rl |
||||
|
||||
from openpilot.system.ui.lib.application import Widget |
||||
from openpilot.system.ui.lib.application import Widget, FontWeight |
||||
from openpilot.system.ui.lib.button import gui_button, ButtonStyle, TextAlignment |
||||
from openpilot.system.ui.lib.label import gui_label |
||||
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel |
||||
|
||||
# Constants |
||||
MARGIN = 50 |
||||
TITLE_FONT_SIZE = 70 |
||||
ITEM_HEIGHT = 135 |
||||
BUTTON_SPACING = 50 |
||||
BUTTON_HEIGHT = 160 |
||||
ITEM_SPACING = 50 |
||||
LIST_ITEM_SPACING = 25 |
||||
|
||||
|
||||
class MultiOptionDialog(Widget): |
||||
def __init__(self, title, options, current=""): |
||||
super().__init__() |
||||
self._title = title |
||||
self._options = options |
||||
self._current = current if current in options else "" |
||||
self._selection = self._current |
||||
self._option_height = 80 |
||||
self._padding = 20 |
||||
self.scroll_panel = GuiScrollPanel() |
||||
|
||||
@property |
||||
def selection(self): |
||||
return self._selection |
||||
self.title = title |
||||
self.options = options |
||||
self.current = current |
||||
self.selection = current |
||||
self.scroll = GuiScrollPanel() |
||||
|
||||
def _render(self, rect): |
||||
title_rect = rl.Rectangle(rect.x + self._padding, rect.y + self._padding, rect.width - 2 * self._padding, 70) |
||||
gui_label(title_rect, self._title, 70) |
||||
|
||||
options_y_start = rect.y + 120 |
||||
options_height = len(self._options) * (self._option_height + 10) |
||||
options_rect = rl.Rectangle(rect.x + self._padding, options_y_start, rect.width - 2 * self._padding, options_height) |
||||
|
||||
view_rect = rl.Rectangle( |
||||
rect.x + self._padding, options_y_start, rect.width - 2 * self._padding, rect.height - 200 - 2 * self._padding |
||||
) |
||||
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)) |
||||
|
||||
offset = self.scroll_panel.handle_scroll(view_rect, options_rect) |
||||
is_click_valid = self.scroll_panel.is_click_valid() |
||||
content_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + MARGIN, |
||||
dialog_rect.width - 2 * MARGIN, dialog_rect.height - 2 * MARGIN) |
||||
|
||||
rl.begin_scissor_mode(int(view_rect.x), int(view_rect.y), int(view_rect.width), int(view_rect.height)) |
||||
gui_label(rl.Rectangle(content_rect.x, content_rect.y, content_rect.width, TITLE_FONT_SIZE), self.title, 70, font_weight=FontWeight.BOLD) |
||||
|
||||
for i, option in enumerate(self._options): |
||||
y_pos = view_rect.y + i * (self._option_height + 10) + offset.y |
||||
item_rect = rl.Rectangle(view_rect.x, y_pos, view_rect.width, self._option_height) |
||||
# Options area |
||||
options_y = content_rect.y + TITLE_FONT_SIZE + ITEM_SPACING |
||||
options_h = content_rect.height - TITLE_FONT_SIZE - BUTTON_HEIGHT - 2 * ITEM_SPACING |
||||
view_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, options_h) |
||||
content_h = len(self.options) * (ITEM_HEIGHT + 10) |
||||
list_content_rect = rl.Rectangle(content_rect.x, options_y, content_rect.width, content_h) |
||||
|
||||
if not rl.check_collision_recs(item_rect, view_rect): |
||||
continue |
||||
# Scroll and render options |
||||
offset = self.scroll.handle_scroll(view_rect, list_content_rect) |
||||
valid_click = self.scroll.is_click_valid() |
||||
|
||||
is_selected = option == self._selection |
||||
button_style = ButtonStyle.PRIMARY if is_selected else ButtonStyle.NORMAL |
||||
rl.begin_scissor_mode(int(view_rect.x), int(options_y), int(view_rect.width), int(options_h)) |
||||
for i, option in enumerate(self.options): |
||||
item_y = options_y + i * (ITEM_HEIGHT + LIST_ITEM_SPACING) + offset.y |
||||
item_rect = rl.Rectangle(view_rect.x, item_y, view_rect.width, ITEM_HEIGHT) |
||||
|
||||
if gui_button(item_rect, option, button_style=button_style, text_alignment=TextAlignment.LEFT) and is_click_valid: |
||||
self._selection = option |
||||
if rl.check_collision_recs(item_rect, view_rect): |
||||
selected = option == self.selection |
||||
style = ButtonStyle.PRIMARY if selected else ButtonStyle.NORMAL |
||||
|
||||
if gui_button(item_rect, option, button_style=style, text_alignment=TextAlignment.LEFT) and valid_click: |
||||
self.selection = option |
||||
rl.end_scissor_mode() |
||||
|
||||
button_y = rect.y + rect.height - 80 - self._padding |
||||
button_width = (rect.width - 3 * self._padding) / 2 |
||||
|
||||
cancel_rect = rl.Rectangle(rect.x + self._padding, button_y, button_width, 80) |
||||
if gui_button(cancel_rect, "Cancel"): |
||||
return 0 # Canceled |
||||
|
||||
select_rect = rl.Rectangle(rect.x + 2 * self._padding + button_width, button_y, button_width, 80) |
||||
has_new_selection = self._selection != "" and self._selection != self._current |
||||
|
||||
if gui_button(select_rect, "Select", is_enabled=has_new_selection, button_style=ButtonStyle.PRIMARY): |
||||
return 1 # Selected |
||||
|
||||
return -1 # Still active |
||||
|
||||
# Buttons |
||||
button_y = content_rect.y + content_rect.height - BUTTON_HEIGHT |
||||
button_w = (content_rect.width - BUTTON_SPACING) / 2 |
||||
|
||||
if __name__ == "__main__": |
||||
from openpilot.system.ui.lib.application import gui_app |
||||
if gui_button(rl.Rectangle(content_rect.x, button_y, button_w, BUTTON_HEIGHT), "Cancel"): |
||||
return 0 |
||||
|
||||
gui_app.init_window("Multi Option Dialog Example") |
||||
options = [f"Option {i}" for i in range(1, 11)] |
||||
dialog = MultiOptionDialog("Choose an option", options, options[0]) |
||||
if gui_button(rl.Rectangle(content_rect.x + button_w + BUTTON_SPACING, button_y, button_w, BUTTON_HEIGHT), |
||||
"Select", is_enabled=self.selection != self.current, button_style=ButtonStyle.PRIMARY): |
||||
return 1 |
||||
|
||||
for _ in gui_app.render(): |
||||
result = dialog.render(rl.Rectangle(100, 100, 1024, 800)) |
||||
if isinstance(result, int) and result >= 0: |
||||
print(f"Selected: {dialog.selection}" if result > 0 else "Canceled") |
||||
break |
||||
return -1 |
||||
|
Loading…
Reference in new issue