-
Notifications
You must be signed in to change notification settings - Fork 9.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ui: Initial UI rewrite using pyray (spinner and text window) (#34583)
* pyray init version * remove c++ code * cleanup * restruct the directory layout * improve GuiApplication * smooth out the texture after resize * use atexit to close app * rename FontSize->FontWeight * make files executable * use Inter Regular for FrontWeight.NORMAL * set FLAG_VSYNC_HINT to avoid tearing while scrolling * smoother scrolling * mange textures in gui_app
- Loading branch information
Showing
16 changed files
with
327 additions
and
192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import atexit | ||
import os | ||
import pyray as rl | ||
from enum import IntEnum | ||
from openpilot.common.basedir import BASEDIR | ||
|
||
DEFAULT_TEXT_SIZE = 60 | ||
DEFAULT_FPS = 60 | ||
FONT_DIR = os.path.join(BASEDIR, "selfdrive/assets/fonts") | ||
|
||
class FontWeight(IntEnum): | ||
BLACK = 0 | ||
BOLD = 1 | ||
EXTRA_BOLD = 2 | ||
EXTRA_LIGHT = 3 | ||
MEDIUM = 4 | ||
NORMAL = 5 | ||
SEMI_BOLD= 6 | ||
THIN = 7 | ||
|
||
|
||
class GuiApplication: | ||
def __init__(self, width: int, height: int): | ||
self._fonts: dict[FontWeight, rl.Font] = {} | ||
self._width = width | ||
self._height = height | ||
self._textures: list[rl.Texture] = [] | ||
|
||
def init_window(self, title: str, fps: int=DEFAULT_FPS): | ||
atexit.register(self.close) # Automatically call close() on exit | ||
|
||
rl.set_config_flags(rl.ConfigFlags.FLAG_MSAA_4X_HINT | rl.ConfigFlags.FLAG_VSYNC_HINT) | ||
rl.init_window(self._width, self._height, title) | ||
rl.set_target_fps(fps) | ||
|
||
self._set_styles() | ||
self._load_fonts() | ||
|
||
def load_texture_from_image(self, file_name: str, width: int, height: int): | ||
"""Load and resize a texture, storing it for later automatic unloading.""" | ||
image = rl.load_image(file_name) | ||
rl.image_resize(image, width, height) | ||
texture = rl.load_texture_from_image(image) | ||
# Set texture filtering to smooth the result | ||
rl.set_texture_filter(texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) | ||
|
||
rl.unload_image(image) | ||
|
||
self._textures.append(texture) | ||
return texture | ||
|
||
def close(self): | ||
for texture in self._textures: | ||
rl.unload_texture(texture) | ||
|
||
for font in self._fonts.values(): | ||
rl.unload_font(font) | ||
|
||
rl.close_window() | ||
|
||
def font(self, font_wight: FontWeight=FontWeight.NORMAL): | ||
return self._fonts[font_wight] | ||
|
||
@property | ||
def width(self): | ||
return self._width | ||
|
||
@property | ||
def height(self): | ||
return self._height | ||
|
||
def _load_fonts(self): | ||
font_files = ( | ||
"Inter-Black.ttf", | ||
"Inter-Bold.ttf", | ||
"Inter-ExtraBold.ttf", | ||
"Inter-ExtraLight.ttf", | ||
"Inter-Medium.ttf", | ||
"Inter-Regular.ttf", | ||
"Inter-SemiBold.ttf", | ||
"Inter-Thin.ttf" | ||
) | ||
|
||
for index, font_file in enumerate(font_files): | ||
font = rl.load_font_ex(os.path.join(FONT_DIR, font_file), 120, None, 0) | ||
rl.set_texture_filter(font.texture, rl.TextureFilter.TEXTURE_FILTER_BILINEAR) | ||
self._fonts[index] = font | ||
|
||
rl.gui_set_font(self._fonts[FontWeight.NORMAL]) | ||
|
||
def _set_styles(self): | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BORDER_WIDTH, 0) | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, DEFAULT_TEXT_SIZE) | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.BACKGROUND_COLOR, rl.color_to_int(rl.BLACK)) | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.TEXT_COLOR_NORMAL, rl.color_to_int(rl.Color(200, 200, 200, 255))) | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.BACKGROUND_COLOR, rl.color_to_int(rl.Color(30, 30, 30, 255))) | ||
rl.gui_set_style(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(rl.Color(50, 50, 50, 255))) | ||
|
||
|
||
gui_app = GuiApplication(2160, 1080) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
import pyray as rl | ||
from openpilot.system.ui.lib.utils import GuiStyleContext | ||
|
||
BUTTON_DEFAULT_BG_COLOR = rl.Color(51, 51, 51, 255) | ||
|
||
def gui_button(rect, text, bg_color=BUTTON_DEFAULT_BG_COLOR): | ||
styles = [ | ||
(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE), | ||
(rl.GuiControl.DEFAULT, rl.GuiControlProperty.BASE_COLOR_NORMAL, rl.color_to_int(bg_color)) | ||
] | ||
|
||
with GuiStyleContext(styles): | ||
return rl.gui_button(rect, text) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import pyray as rl | ||
from openpilot.system.ui.lib.utils import GuiStyleContext | ||
|
||
def gui_label(rect, text, font_size): | ||
styles = [ | ||
(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_SIZE, font_size), | ||
(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_LINE_SPACING, font_size), | ||
(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_ALIGNMENT_VERTICAL, rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP), | ||
(rl.GuiControl.DEFAULT, rl.GuiDefaultProperty.TEXT_WRAP_MODE, rl.GuiTextWrapMode.TEXT_WRAP_WORD) | ||
] | ||
|
||
with GuiStyleContext(styles): | ||
rl.gui_label(rect, text) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import pyray as rl | ||
from cffi import FFI | ||
|
||
MOUSE_WHEEL_SCROLL_SPEED = 30 | ||
|
||
class GuiScrollPanel: | ||
def __init__(self, bounds: rl.Rectangle, content: rl.Rectangle, show_vertical_scroll_bar: bool = False): | ||
self._dragging: bool = False | ||
self._last_mouse_y: float = 0.0 | ||
self._bounds = bounds | ||
self._content = content | ||
self._scroll = rl.Vector2(0, 0) | ||
self._view = rl.Rectangle(0, 0, 0, 0) | ||
self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar | ||
|
||
def handle_scroll(self)-> rl.Vector2: | ||
mouse_pos = rl.get_mouse_position() | ||
if rl.check_collision_point_rec(mouse_pos, self._bounds) and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT): | ||
if not self._dragging: | ||
self._dragging = True | ||
self._last_mouse_y = mouse_pos.y | ||
|
||
if self._dragging: | ||
if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT): | ||
delta_y = mouse_pos.y - self._last_mouse_y | ||
self._scroll.y += delta_y | ||
self._last_mouse_y = mouse_pos.y | ||
else: | ||
self._dragging = False | ||
|
||
wheel_move = rl.get_mouse_wheel_move() | ||
if self._show_vertical_scroll_bar: | ||
self._scroll.y += wheel_move * (MOUSE_WHEEL_SCROLL_SPEED - 20) | ||
rl.gui_scroll_panel(self._bounds, FFI().NULL, self._content, self._scroll, self._view) | ||
else: | ||
self._scroll.y += wheel_move * MOUSE_WHEEL_SCROLL_SPEED | ||
max_scroll_y = self._content.height - self._bounds.height | ||
self._scroll.y = max(min(self._scroll.y, 0), -max_scroll_y) | ||
|
||
return self._scroll |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import pyray as rl | ||
|
||
class GuiStyleContext: | ||
def __init__(self, styles: list[tuple[int, int, int]]): | ||
"""styles is a list of tuples (control, prop, new_value)""" | ||
self.styles = styles | ||
self.prev_styles: list[tuple[int, int, int]] = [] | ||
|
||
def __enter__(self): | ||
for control, prop, new_value in self.styles: | ||
prev_value = rl.gui_get_style(control, prop) | ||
self.prev_styles.append((control, prop, prev_value)) | ||
rl.gui_set_style(control, prop, new_value) | ||
|
||
def __exit__(self, exc_type, exc_value, traceback): | ||
for control, prop, prev_value in self.prev_styles: | ||
rl.gui_set_style(control, prop, prev_value) |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.