BigUI WIP: Aethergrid Update

This commit is contained in:
firestarsdog
2026-06-10 00:07:00 -04:00
parent f93c536f7e
commit cf7822180b
@@ -2808,6 +2808,46 @@ class AetherTile(Widget):
return layout
def _render_hud_background(self, rect: rl.Rectangle, accent: rl.Color, glow: float = 1.0) -> tuple[rl.Rectangle, rl.Color]:
sq = getattr(self, '_squish', 1.0)
snapped = _snap_rect(rect)
sw = snapped.width * sq
sh = snapped.height * sq
ox = snapped.x + (snapped.width - sw) / 2
oy = snapped.y + (snapped.height - sh) / 2
rx, ry, rw, rh = int(ox), int(oy), int(sw), int(sh)
face = rl.Rectangle(rx, ry, rw, rh)
off_border = _HUD_BORDER_OFF
for i in range(4, 0, -1):
off = i * 2.5 * glow
gr = rl.Rectangle(rx - off, ry - off, rw + off * 2, rh + off * 2)
a = int(25 * (1.0 - i / 5) * glow)
_draw_rounded_fill(gr, rl.Color(accent.r, accent.g, accent.b, a), radius_px=100)
_draw_rounded_fill(face, _HUD_BG_ON, radius_px=100)
bc = rl.Color(
int(off_border.r + (accent.r - off_border.r) * glow),
int(off_border.g + (accent.g - off_border.g) * glow),
int(off_border.b + (accent.b - off_border.b) * glow),
255)
_draw_rounded_stroke(face, bc, radius_px=100)
led_w, led_h = 32, 2
led_x = rx + (rw - led_w) // 2
led_y = ry + 12
led_base = _HUD_LED_BASE
led_col = rl.Color(
int(led_base.r + (accent.r - led_base.r) * glow),
int(led_base.g + (accent.g - led_base.g) * glow),
int(led_base.b + (accent.b - led_base.b) * glow),
255)
rl.draw_rectangle(led_x, led_y, led_w, led_h, led_col)
return face, accent
def _render(self, rect: rl.Rectangle):
pass
@@ -2833,41 +2873,83 @@ class HubTile(AetherTile):
self._icon = None
self._font_title = gui_app.font(FontWeight.BOLD)
self._font_desc = gui_app.font(FontWeight.NORMAL)
self._squish: float = 1.0
def _update_state(self):
dt = rl.get_frame_time()
if self._squish < 1.0:
self._squish += (1.0 - self._squish) * 15.0 * dt
def _handle_mouse_press(self, mouse_pos: MousePos):
if not self.enabled:
return
if rl.check_collision_point_rec(mouse_pos, self._hit_rect):
self._is_pressed = True
self._plate_target = 1.0
self._squish = 0.95
def _render(self, rect: rl.Rectangle):
face = self._render_layers(rect)
self._draw_signal_edge(face, self.surface_color, alpha=48)
self._animate_plate(rl.get_frame_time())
status_text = self.get_status() if self.get_status else ""
title_text = str(_resolve_value(self.title, ""))
fallback_desc = str(_resolve_value(self.desc, ""))
face, accent = self._render_hud_background(rect, self.surface_color)
rx, ry, rw, rh = face.x, face.y, face.width, face.height
content_pad = SPACING.tile_content
max_w = rw - content_pad * 2
text_scale = min(rw / 360.0, rh / 205.0)
gap = SPACING.line_gap
title_size = max(18, int(round(22 * text_scale)))
desc_to_render = status_text if status_text else fallback_desc
desc_size = max(12, int(round(14 * text_scale))) if desc_to_render else 0
icon_h = 0.0
if self.custom_icon_key:
icon_base_size = 100.0
icon_scale_mult = 1.25
icon_canvas_size = 60.0
icon_scale = min(0.80, max(0.56, text_scale * 0.72))
icon_h = icon_base_size * icon_scale_mult * icon_scale
total_h = icon_h + (gap if icon_h > 0 else 0) + title_size + (gap if desc_to_render else 0) + desc_size
content_top = ry + max(0, (rh - total_h) / 2)
if self.custom_icon_key:
icon_base_size = 100.0
icon_scale_mult = 1.25
icon_canvas_size = 60.0
icon_scale = min(0.80, max(0.56, text_scale * 0.72))
icon_width = icon_base_size * icon_scale_mult * icon_scale
icon_x = rx + (rw - icon_width) / 2
s = icon_scale * (icon_base_size / icon_canvas_size) * icon_scale_mult
self._draw_custom_icon(self.custom_icon_key, icon_x, content_top, s, _mix_colors(rl.Color(255, 255, 255, 255), accent, 0.08))
content_top += icon_h + gap
self._draw_text_fit(self._font_title, title_text,
rl.Vector2(rx + content_pad, content_top),
max_w, title_size, align_center=True, color=rl.WHITE)
content_top += title_size
if desc_to_render:
content_top += gap
self._draw_text_fit(self._font_desc, desc_to_render,
rl.Vector2(rx + content_pad, content_top),
max_w, desc_size, align_center=True, color=_HUD_TEXT_DIM)
if status_text:
import re
m = re.search(r'(\d+)%$', status_text)
if m:
ratio = min(1.0, max(0.0, float(m.group(1)) / 100.0))
if ratio > 0.05:
meter_h = 6
meter_rect = rl.Rectangle(face.x + SPACING.tile_content, face.y + face.height - SPACING.tile_content - meter_h, face.width - SPACING.tile_content * 2, meter_h)
meter_rect = rl.Rectangle(rx + content_pad, ry + rh - content_pad - meter_h, rw - content_pad * 2, meter_h)
fill_rect = rl.Rectangle(meter_rect.x, meter_rect.y, meter_rect.width * ratio, meter_h)
rl.draw_rectangle_rec(_snap_rect(meter_rect), rl.Color(255, 255, 255, 14))
rl.draw_rectangle_rec(_snap_rect(fill_rect), _with_alpha(self.surface_color, 170))
desc_to_render = status_text if status_text else fallback_desc
self._render_tile_stack(
face,
icon=self._icon,
title=title_text,
primary=desc_to_render,
desc="",
title_font=self._font_title,
primary_font=self._font_desc,
desc_font=self._font_desc,
title_size=30,
primary_size=18,
custom_icon_key=self.custom_icon_key,
)
rl.draw_rectangle_rec(_snap_rect(fill_rect), _with_alpha(accent, 170))
class AetherSelectionTile(AetherTile):
@@ -2999,64 +3081,21 @@ class ToggleTile(AetherTile):
return
# --- HUD toggle path (show_led + enabled) ---
g = self._glow
sq = self._squish
snapped = _snap_rect(rect)
sw = snapped.width * sq
sh = snapped.height * sq
ox = snapped.x + (snapped.width - sw) / 2
oy = snapped.y + (snapped.height - sh) / 2
rx, ry, rw, rh = int(ox), int(oy), int(sw), int(sh)
face = rl.Rectangle(rx, ry, rw, rh)
face, accent = self._render_hud_background(rect, self._active_color, self._glow)
rx, ry, rw, rh = face.x, face.y, face.width, face.height
accent = self._active_color
off_border = _HUD_BORDER_OFF
# Concentric glow layers (outermost first)
for i in range(4, 0, -1):
off = i * 2.5 * g
gr = rl.Rectangle(rx - off, ry - off, rw + off * 2, rh + off * 2)
a = int(25 * (1.0 - i / 5) * g)
_draw_rounded_fill(gr, rl.Color(accent.r, accent.g, accent.b, a), radius_px=100)
# Panel fill
panel = rl.Color(12, 10, 18, 230) if active else _HUD_BG_ON
_draw_rounded_fill(face, panel, radius_px=100)
# Glowing border — interpolates off → accent
bc = rl.Color(
int(off_border.r + (accent.r - off_border.r) * g),
int(off_border.g + (accent.g - off_border.g) * g),
int(off_border.b + (accent.b - off_border.b) * g),
255)
_draw_rounded_stroke(face, bc, radius_px=100)
# Compact LED bar (centered pill at top)
led_w, led_h = 32, 2
led_x = rx + (rw - led_w) // 2
led_y = ry + 12
led_base = _HUD_LED_BASE
led_col = rl.Color(
int(led_base.r + (accent.r - led_base.r) * g),
int(led_base.g + (accent.g - led_base.g) * g),
int(led_base.b + (accent.b - led_base.b) * g),
255)
rl.draw_rectangle(led_x, led_y, led_w, led_h, led_col)
# Title text
content_pad = SPACING.tile_content
max_w = rw - content_pad * 2
text_scale = min(rw / 360.0, rh / 205.0)
title_size = max(18, int(round(22 * text_scale)))
title_color = rl.WHITE if active else _HUD_TEXT_DIM
title_color = rl.WHITE if self.get_state() else _HUD_TEXT_DIM
self._draw_text_fit(self._font, self.title,
rl.Vector2(rx + content_pad, ry + int(rh * 0.50)),
max_w, title_size, align_center=True, color=title_color)
# Status LED
led_cx = rx + rw // 2
led_cy = ry + int(rh * 0.75)
if active:
if self.get_state():
rl.draw_circle(int(led_cx), int(led_cy), 11, rl.Color(accent.r, accent.g, accent.b, 24))
rl.draw_circle(int(led_cx), int(led_cy), 6, accent)
else:
@@ -3083,27 +3122,52 @@ class ValueTile(AetherTile):
self._font_desc = gui_app.font(FontWeight.NORMAL)
self._active_color = self.surface_color
self._disabled_color = rl.Color(120, 120, 120, 255)
self._squish: float = 1.0
def _update_state(self):
dt = rl.get_frame_time()
if self._squish < 1.0:
self._squish += (1.0 - self._squish) * 15.0 * dt
def _handle_mouse_press(self, mouse_pos: MousePos):
if not self.enabled:
return
if rl.check_collision_point_rec(mouse_pos, self._hit_rect):
self._is_pressed = True
self._plate_target = 1.0
self._squish = 0.95
def _render(self, rect: rl.Rectangle):
enabled = self.enabled
self.surface_color = self._active_color if enabled else self._disabled_color
self._animate_plate(rl.get_frame_time())
if not enabled:
self.surface_color = self._disabled_color
self._plate_offset = 0.0
self._plate_target = 0.0
face = self._render_layers(rect)
face = self._render_layers(rect)
self._draw_signal_edge(face, self.surface_color, width=TILE_SIGNAL_WIDTH, alpha=28)
self._render_tile_stack(face, title=self.title, primary=self.get_value(), desc=self.desc,
title_font=self._font, primary_font=self._font, desc_font=self._font_desc,
title_size=28, primary_size=28)
return
face, accent = self._render_hud_background(rect, self._active_color)
rx, ry, rw, rh = face.x, face.y, face.width, face.height
content_pad = SPACING.tile_content
max_w = rw - content_pad * 2
text_scale = min(rw / 360.0, rh / 205.0)
title_size = max(18, int(round(22 * text_scale)))
self._draw_text_fit(self._font, self.title,
rl.Vector2(rx + content_pad, ry + int(rh * 0.35)),
max_w, title_size, align_center=True, color=_HUD_TEXT_DIM)
val_text = self.get_value()
self._draw_signal_edge(face, self._active_color if enabled else self._disabled_color, width=TILE_SIGNAL_WIDTH, alpha=38 if enabled else 20)
self._render_tile_stack(
face,
title=self.title,
primary=val_text,
desc=self.desc,
title_font=self._font,
primary_font=self._font,
desc_font=self._font_desc,
title_size=28,
primary_size=28,
)
val_size = max(18, int(round(24 * text_scale)))
self._draw_text_fit(self._font, val_text,
rl.Vector2(rx + content_pad, ry + int(rh * 0.58)),
max_w, val_size, align_center=True, color=accent)
class SliderTile(AetherTile):
@@ -3149,10 +3213,17 @@ class SliderTile(AetherTile):
self._press_start_x = 0.0
self._press_start_time: float | None = None
self._long_press_triggered = False
self._squish: float = 1.0
def _update_state(self):
dt = rl.get_frame_time()
if self._squish < 1.0:
self._squish += (1.0 - self._squish) * 15.0 * dt
def _handle_mouse_press(self, mouse_pos: MousePos):
if rl.check_collision_point_rec(mouse_pos, self._hit_rect) and self.enabled:
self._is_pressed = True
self._squish = 0.95
self._last_mouse_x = mouse_pos.x
self._velocity = 0.0
self._press_start_x = mouse_pos.x
@@ -3213,37 +3284,46 @@ class SliderTile(AetherTile):
dt = rl.get_frame_time()
self._smooth_value += (current_val - self._smooth_value) * (1 - math.exp(-dt / 0.1))
self._animate_plate(dt)
self.surface_color = self._active_color if enabled else self._disabled_color
if not enabled:
self.surface_color = self._disabled_color
self._plate_offset = 0.0
self._plate_target = 0.0
face = self._render_layers(rect)
self._draw_signal_edge(face, self.surface_color, width=TILE_SIGNAL_WIDTH, alpha=28)
val_str = self.labels.get(current_val, f"{int(current_val)}{self.unit}")
self._render_tile_stack(face, title=self.title, primary=val_str, desc=self.desc,
title_font=self._font, primary_font=self._font, desc_font=self._font_desc,
title_size=28, primary_size=28)
return
face = self._render_layers(rect)
self._draw_signal_edge(face, self.surface_color, width=TILE_SIGNAL_WIDTH, alpha=38 if enabled else 20)
face, accent = self._render_hud_background(rect, self._active_color)
rx, ry, rw, rh = face.x, face.y, face.width, face.height
content_pad = SPACING.tile_content
max_w = rw - content_pad * 2
text_scale = min(rw / 360.0, rh / 205.0)
title_size = max(18, int(round(22 * text_scale)))
self._draw_text_fit(self._font, self.title,
rl.Vector2(rx + content_pad, ry + int(rh * 0.30)),
max_w, title_size, align_center=True, color=_HUD_TEXT_DIM)
val_str = self.labels.get(current_val, f"{int(current_val)}{self.unit}")
val_size = max(18, int(round(24 * text_scale)))
self._draw_text_fit(self._font, val_str,
rl.Vector2(rx + content_pad, ry + int(rh * 0.52)),
max_w, val_size, align_center=True, color=accent)
value_range = self.max_val - self.min_val
frac = 0.0 if value_range == 0 else max(0.0, min(1.0, (self._smooth_value - self.min_val) / value_range))
meter_h = 6
meter_rect = rl.Rectangle(face.x + SPACING.tile_content, face.y + face.height - SPACING.tile_content - meter_h, face.width - SPACING.tile_content * 2, meter_h)
meter_rect = rl.Rectangle(rx + content_pad, ry + rh - content_pad - meter_h, rw - content_pad * 2, meter_h)
fill_w = meter_rect.width * frac
rl.draw_rectangle_rec(_snap_rect(meter_rect), rl.Color(255, 255, 255, 14))
if fill_w > 1:
fill_rect = rl.Rectangle(meter_rect.x, meter_rect.y, fill_w, meter_rect.height)
rl.draw_rectangle_rec(_snap_rect(fill_rect), _with_alpha(self.surface_color, 176))
val_str = self.labels.get(current_val, f"{int(current_val)}{self.unit}")
self._render_tile_stack(
face,
title=self.title,
primary=val_str,
desc=self.desc,
title_font=self._font,
primary_font=self._font,
desc_font=self._font_desc,
title_size=28,
primary_size=28,
)
rl.draw_rectangle_rec(_snap_rect(fill_rect), _with_alpha(accent, 176))
class AetherSlider(Widget):