Files
onepilot/tsk/ui/header.py
T
2025-09-06 21:02:37 -07:00

120 lines
5.7 KiB
Python

# tsk/ui/header.py
from typing import Optional
import pyray as rl
from openpilot.system.ui.lib.application import gui_app
from tsk.common.key_file_manager import KeyFileManager
from tsk.ui.layout import Theme # Import Theme
class Header:
"""Renders the top navigation bar with title and key status."""
def __init__(self):
"""Initializes the Header."""
self.key_manager = KeyFileManager()
def _draw_title(self, rect: rl.Rectangle, current_menu: int) -> None:
"""Draws the title strip."""
title_text_color = Theme.brighten_color(Theme.title_bg_color, Theme.brighten_amount)
menu_name = Theme.menu_names.get(current_menu, "Unknown Menu")
full_title_text = "TSK Manager: " + menu_name
full_title_text_size = rl.measure_text_ex(gui_app.font(), full_title_text, Theme.title_font_size, 1.0)
title_text_prefix_size = rl.measure_text_ex(gui_app.font(), "TSK Manager: ", Theme.title_font_size, 1.0)
menu_name_x = gui_app.width * Theme.title_prefix_x_offset_percent + title_text_prefix_size.x + Theme.title_x_offset
text_y = (self._calculate_height() - full_title_text_size.y) / 2
rl.draw_text_ex(gui_app.font(), "TSK Manager: ", rl.Vector2(gui_app.width * Theme.title_prefix_x_offset_percent + Theme.title_x_offset, text_y),
Theme.title_font_size, 1.0, title_text_color)
rl.draw_text_ex(gui_app.font(), menu_name, rl.Vector2(menu_name_x, text_y), Theme.title_font_size, 1.0, title_text_color)
def _draw_navigation_buttons(self, rect: rl.Rectangle, current_menu: int) -> Optional[int]:
"""Draws the navigation buttons and handles button clicks."""
nav_button_width = self._calculate_nav_button_width()
left_button_x = 0
left_button_rect = rl.Rectangle(left_button_x, 0, nav_button_width, rect.height)
right_button_x = gui_app.width - nav_button_width
right_button_rect = rl.Rectangle(right_button_x, 0, nav_button_width, rect.height)
new_menu = None
def handle_button(button_rect: rl.Rectangle, text: str, target_menu: int) -> Optional[int]:
"""Handles drawing, input, and logic for a single navigation button."""
mouse_pos = rl.get_mouse_position()
is_hovering = rl.check_collision_point_rec(mouse_pos, button_rect)
is_pressed = is_hovering and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT)
rl.draw_rectangle_rec(button_rect, Theme.button_color)
lines = text.splitlines()
total_text_height = len(lines) * rl.measure_text_ex(gui_app.font(), "A", Theme.nav_button_font_size, 1.0).y
start_y = rect.y + (rect.height - total_text_height) / 2
for i, line in enumerate(lines):
text_size = rl.measure_text_ex(gui_app.font(), line, Theme.nav_button_font_size, 1.0)
text_x = button_rect.x + (button_rect.width - text_size.x) / 2
text_y = start_y + i * text_size.y
font_color = Theme.brighten_color(rl.Color(100, 100, 100, 255), Theme.brighten_amount) # Use the same color as other buttons
rl.draw_text_ex(gui_app.font(), line, rl.Vector2(text_x, text_y), Theme.nav_button_font_size, 1.0, font_color)
if is_pressed:
return target_menu
return None
if current_menu == Theme.menu_reboot:
new_menu = handle_button(left_button_rect, Theme.nav_button_text_left, Theme.menu_tools)
# --- Handle Right Button ---
if current_menu == Theme.menu_tools:
right_menu = handle_button(right_button_rect, Theme.nav_button_text_right, Theme.menu_reboot)
if new_menu is None:
new_menu = right_menu
return new_menu
def _draw_key_status(self, rect: rl.Rectangle) -> None:
"""Draws the key status label."""
key_status_text = f"Key installed: {self.key_manager.installed_key}" if self.key_manager.installed_key else "Key not installed"
status_text_x = (gui_app.width - rl.measure_text_ex(gui_app.font(), key_status_text, Theme.key_status_font_size, 1.0).x) / 2
status_text_y = rect.y + (rect.height - rl.measure_text_ex(gui_app.font(), key_status_text, Theme.key_status_font_size, 1.0).y) / 2 # Use rect.y
rl.draw_text_ex(gui_app.font(), key_status_text, rl.Vector2(status_text_x, status_text_y), Theme.key_status_font_size, 1.0, Theme.key_text_color)
def _calculate_height(self) -> float:
"""Calculates the height of the header."""
title_text_prefix = "TSK Manager: "
return rl.measure_text_ex(gui_app.font(), title_text_prefix, Theme.title_font_size, 1.0).y * 1.5
def _calculate_nav_button_width(self) -> float:
"""Calculates the width of the navigation buttons."""
return max(rl.measure_text_ex(gui_app.font(), Theme.nav_button_text_left, Theme.nav_button_font_size, 1.0).x,
rl.measure_text_ex(gui_app.font(), Theme.nav_button_text_right, Theme.nav_button_font_size, 1.0).x) + 10
def render(self, rect: rl.Rectangle, current_menu: int) -> Optional[int]:
"""Renders the top strip with the title and menu navigation."""
title_height = self._calculate_height()
key_status_height = Theme.key_status_font_size * 1.5 # Approximate height
# --- Draw Title Strip ---
title_rect = rl.Rectangle(0, 0, gui_app.width, title_height)
rl.draw_rectangle_rec(title_rect, Theme.title_bg_color)
self._draw_title(rect, current_menu)
# --- Draw Key Status Strip ---
key_status_y = title_height
key_status_rect = rl.Rectangle(0, key_status_y, gui_app.width, key_status_height)
rl.draw_rectangle_rec(key_status_rect, Theme.nav_bg_color) # Use nav_bg_color for key status
self._draw_key_status(key_status_rect)
# --- Draw Navigation Buttons (Overlapping) ---
nav_rect = rl.Rectangle(0, 0, gui_app.width, title_height + key_status_height) # Overlap both
new_menu = self._draw_navigation_buttons(nav_rect, current_menu)
if new_menu is not None:
return new_menu
return None