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 | import pyray as rl | ||||||
| 
 | from openpilot.system.ui.lib.application import Widget, FontWeight | ||||||
| from openpilot.system.ui.lib.application import Widget |  | ||||||
| from openpilot.system.ui.lib.button import gui_button, ButtonStyle, TextAlignment | 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.label import gui_label | ||||||
| from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel | 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): | class MultiOptionDialog(Widget): | ||||||
|   def __init__(self, title, options, current=""): |   def __init__(self, title, options, current=""): | ||||||
|     super().__init__() |     super().__init__() | ||||||
|     self._title = title |     self.title = title | ||||||
|     self._options = options |     self.options = options | ||||||
|     self._current = current if current in options else "" |     self.current = current | ||||||
|     self._selection = self._current |     self.selection = current | ||||||
|     self._option_height = 80 |     self.scroll = GuiScrollPanel() | ||||||
|     self._padding = 20 |  | ||||||
|     self.scroll_panel = GuiScrollPanel() |  | ||||||
| 
 |  | ||||||
|   @property |  | ||||||
|   def selection(self): |  | ||||||
|     return self._selection |  | ||||||
| 
 | 
 | ||||||
|   def _render(self, rect): |   def _render(self, rect): | ||||||
|     title_rect = rl.Rectangle(rect.x + self._padding, rect.y + self._padding, rect.width - 2 * self._padding, 70) |     dialog_rect = rl.Rectangle(rect.x + MARGIN, rect.y + MARGIN, rect.width - 2 * MARGIN, rect.height - 2 * MARGIN) | ||||||
|     gui_label(title_rect, self._title, 70) |     rl.draw_rectangle_rounded(dialog_rect, 0.02, 20, rl.Color(30, 30, 30, 255)) | ||||||
| 
 |  | ||||||
|     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 |  | ||||||
|     ) |  | ||||||
| 
 | 
 | ||||||
|     offset = self.scroll_panel.handle_scroll(view_rect, options_rect) |     content_rect = rl.Rectangle(dialog_rect.x + MARGIN, dialog_rect.y + MARGIN, | ||||||
|     is_click_valid = self.scroll_panel.is_click_valid() |                                 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): |     # Options area | ||||||
|       y_pos = view_rect.y + i * (self._option_height + 10) + offset.y |     options_y = content_rect.y + TITLE_FONT_SIZE + ITEM_SPACING | ||||||
|       item_rect = rl.Rectangle(view_rect.x, y_pos, view_rect.width, self._option_height) |     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): |     # Scroll and render options | ||||||
|         continue |     offset = self.scroll.handle_scroll(view_rect, list_content_rect) | ||||||
|  |     valid_click = self.scroll.is_click_valid() | ||||||
| 
 | 
 | ||||||
|       is_selected = option == self._selection |     rl.begin_scissor_mode(int(view_rect.x), int(options_y), int(view_rect.width), int(options_h)) | ||||||
|       button_style = ButtonStyle.PRIMARY if is_selected else ButtonStyle.NORMAL |     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: |       if rl.check_collision_recs(item_rect, view_rect): | ||||||
|         self._selection = option |         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() |     rl.end_scissor_mode() | ||||||
| 
 | 
 | ||||||
|     button_y = rect.y + rect.height - 80 - self._padding |     # Buttons | ||||||
|     button_width = (rect.width - 3 * self._padding) / 2 |     button_y = content_rect.y + content_rect.height - BUTTON_HEIGHT | ||||||
| 
 |     button_w = (content_rect.width - BUTTON_SPACING) / 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 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": |     if gui_button(rl.Rectangle(content_rect.x, button_y, button_w, BUTTON_HEIGHT), "Cancel"): | ||||||
|   from openpilot.system.ui.lib.application import gui_app |       return 0 | ||||||
| 
 | 
 | ||||||
|   gui_app.init_window("Multi Option Dialog Example") |     if gui_button(rl.Rectangle(content_rect.x + button_w + BUTTON_SPACING, button_y, button_w, BUTTON_HEIGHT), | ||||||
|   options = [f"Option {i}" for i in range(1, 11)] |                  "Select", is_enabled=self.selection != self.current, button_style=ButtonStyle.PRIMARY): | ||||||
|   dialog = MultiOptionDialog("Choose an option", options, options[0]) |       return 1 | ||||||
| 
 | 
 | ||||||
|   for _ in gui_app.render(): |     return -1 | ||||||
|     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 |  | ||||||
|  | |||||||
					Loading…
					
					
				
		Reference in new issue