193 lines
5.8 KiB
Python
193 lines
5.8 KiB
Python
# tsk/c3/ui/header.py
|
|
"""
|
|
TSK Header widget with navigation and status display.
|
|
|
|
Pure Widget architecture - NO platform detection needed.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
import pyray as rl
|
|
|
|
from openpilot.system.ui.lib.application import gui_app
|
|
from tsk.c3.ui.button import TSKButton
|
|
from tsk.c3.ui.measure_text import measure_text
|
|
from tsk.c3.ui.layout import Theme
|
|
from tsk.common.key_file_manager import KeyFileManager
|
|
from tsk.common.widget import TSKWidget
|
|
|
|
|
|
class TSKHeader(TSKWidget):
|
|
"""
|
|
Header widget for TSK Manager.
|
|
|
|
Displays:
|
|
- Title with current menu name
|
|
- Navigation buttons (left/right based on current menu)
|
|
- Key installation status
|
|
|
|
NO platform detection needed - Widget handles everything.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.key_manager = KeyFileManager()
|
|
self._current_menu = Theme.menu_tools
|
|
self._nav_result = None
|
|
|
|
# Create navigation buttons (TSKButton handles events automatically)
|
|
self._nav_left = TSKButton(
|
|
labels=[
|
|
{"text": "< Tools", "x_offset": 20, "y_offset": 30},
|
|
{"text": "< Menu", "x_offset": 20, "y_offset": 100},
|
|
],
|
|
click_callback=lambda: self._set_nav(Theme.menu_tools),
|
|
font_size=Theme.nav_button_font_size,
|
|
multi_line=True
|
|
)
|
|
|
|
self._nav_right = TSKButton(
|
|
labels=[
|
|
{"text": "Reboot >", "x_offset": 43, "y_offset": 30},
|
|
{"text": "Menu >", "x_offset": 90, "y_offset": 100},
|
|
],
|
|
click_callback=lambda: self._set_nav(Theme.menu_reboot),
|
|
font_size=Theme.nav_button_font_size,
|
|
multi_line=True
|
|
)
|
|
|
|
def _set_nav(self, menu_id: int):
|
|
"""Set navigation result when button is clicked."""
|
|
self._nav_result = menu_id
|
|
|
|
def get_height(self) -> float:
|
|
"""Calculate the total height of the header."""
|
|
title_height = measure_text("TSK Manager: ", Theme.title_font_size).y * 1.5
|
|
key_status_height = Theme.key_status_font_size * 1.5
|
|
return title_height + key_status_height
|
|
|
|
def set_current_menu(self, menu_id: int):
|
|
"""Update the current menu for display."""
|
|
self._current_menu = menu_id
|
|
|
|
def _render(self, rect: rl.Rectangle) -> Optional[int]:
|
|
"""
|
|
Render the header.
|
|
|
|
Returns:
|
|
New menu ID if navigation button was clicked, None otherwise
|
|
"""
|
|
title_height = measure_text("TSK Manager: ", Theme.title_font_size).y * 1.5
|
|
key_status_height = Theme.key_status_font_size * 1.5
|
|
|
|
# Draw title strip
|
|
title_rect = rl.Rectangle(rect.x, rect.y, rect.width, title_height)
|
|
rl.draw_rectangle_rec(title_rect, Theme.title_bg_color)
|
|
self._draw_title(title_rect)
|
|
|
|
# Draw key status strip
|
|
key_status_y = rect.y + title_height
|
|
key_status_rect = rl.Rectangle(
|
|
rect.x,
|
|
key_status_y,
|
|
rect.width,
|
|
key_status_height
|
|
)
|
|
rl.draw_rectangle_rec(key_status_rect, Theme.key_bg_color)
|
|
self._draw_key_status(key_status_rect)
|
|
|
|
# Draw navigation buttons
|
|
self._nav_result = None
|
|
self._draw_navigation_buttons(rect, title_height, key_status_height)
|
|
|
|
return self._nav_result
|
|
|
|
def _draw_title(self, rect: rl.Rectangle):
|
|
"""Draw the title text."""
|
|
# Use white text for header title
|
|
title_text_color = Theme.brighten_color(Theme.title_bg_color, Theme.brighten_amount)
|
|
menu_name = Theme.menu_names.get(self._current_menu, "Unknown Menu")
|
|
prefix = "TSK Manager: "
|
|
|
|
# Measure and position text
|
|
prefix_size = measure_text(prefix, Theme.title_font_size)
|
|
|
|
prefix_x = rect.width * Theme.title_prefix_x_offset_percent + Theme.title_x_offset
|
|
menu_name_x = prefix_x + prefix_size.x
|
|
|
|
full_text = f"{prefix}{menu_name}"
|
|
full_size = measure_text(full_text, Theme.title_font_size)
|
|
text_y = rect.y + (rect.height - full_size.y) / 2
|
|
|
|
# Draw prefix
|
|
rl.draw_text_ex(
|
|
gui_app.font(),
|
|
prefix,
|
|
rl.Vector2(prefix_x, text_y),
|
|
Theme.title_font_size,
|
|
0,
|
|
title_text_color
|
|
)
|
|
|
|
# Draw menu name
|
|
rl.draw_text_ex(
|
|
gui_app.font(),
|
|
menu_name,
|
|
rl.Vector2(menu_name_x, text_y),
|
|
Theme.title_font_size,
|
|
0,
|
|
title_text_color
|
|
)
|
|
|
|
def _draw_key_status(self, rect: rl.Rectangle):
|
|
"""Draw the key installation status."""
|
|
if self.key_manager.installed_key:
|
|
status_text = f"Key installed: {self.key_manager.installed_key}"
|
|
else:
|
|
status_text = "Key not installed"
|
|
|
|
text_size = measure_text(status_text, Theme.key_status_font_size)
|
|
|
|
# Center text
|
|
text_x = rect.x + (rect.width - text_size.x) / 2
|
|
text_y = rect.y + (rect.height - text_size.y) / 2
|
|
|
|
rl.draw_text_ex(
|
|
gui_app.font(),
|
|
status_text,
|
|
rl.Vector2(text_x, text_y),
|
|
Theme.key_status_font_size,
|
|
0,
|
|
Theme.key_text_color
|
|
)
|
|
|
|
def _draw_navigation_buttons(
|
|
self,
|
|
rect: rl.Rectangle,
|
|
title_height: float,
|
|
key_status_height: float
|
|
):
|
|
"""Draw navigation buttons overlapping the header."""
|
|
# Calculate button dimensions
|
|
button_height = title_height + key_status_height
|
|
|
|
# Calculate button width based on text
|
|
left_text_size = measure_text(Theme.nav_button_text_left.replace('\\n', ' '), Theme.nav_button_font_size)
|
|
right_text_size = measure_text(Theme.nav_button_text_right.replace('\\n', ' '), Theme.nav_button_font_size)
|
|
button_width = max(left_text_size.x, right_text_size.x) + 70
|
|
|
|
# Show appropriate button based on current menu
|
|
if self._current_menu == Theme.menu_reboot:
|
|
# Show left button (back to tools)
|
|
left_rect = rl.Rectangle(rect.x, rect.y, button_width, button_height)
|
|
self._nav_left.render(left_rect)
|
|
elif self._current_menu == Theme.menu_tools:
|
|
# Show right button (go to reboot)
|
|
right_rect = rl.Rectangle(
|
|
rect.x + rect.width - button_width,
|
|
rect.y,
|
|
button_width,
|
|
button_height
|
|
)
|
|
self._nav_right.render(right_rect)
|