107 lines
3.8 KiB
Rust
107 lines
3.8 KiB
Rust
/// Theme rendering helpers for painting CSS backgrounds
|
|
///
|
|
/// Handles solid colors, linear gradients (via egui Mesh), and image backgrounds.
|
|
|
|
use eframe::egui;
|
|
|
|
use crate::theme::Background;
|
|
|
|
/// Paint a background into the given rect
|
|
pub fn paint_background(
|
|
painter: &egui::Painter,
|
|
rect: egui::Rect,
|
|
background: &Background,
|
|
rounding: f32,
|
|
) {
|
|
match background {
|
|
Background::Solid(color) => {
|
|
painter.rect_filled(rect, rounding, *color);
|
|
}
|
|
Background::LinearGradient { angle_degrees, stops } => {
|
|
paint_linear_gradient(painter, rect, *angle_degrees, stops, rounding);
|
|
}
|
|
Background::Image { .. } => {
|
|
// Image backgrounds require a TextureHandle loaded externally.
|
|
// For now, fall back to transparent (no-op).
|
|
// TODO: image cache integration
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Paint a linear gradient using an egui Mesh with colored vertices
|
|
///
|
|
/// Supports arbitrary angles. The gradient direction follows CSS conventions:
|
|
/// - 0deg = bottom to top
|
|
/// - 90deg = left to right
|
|
/// - 180deg = top to bottom (default)
|
|
/// - 270deg = right to left
|
|
pub fn paint_linear_gradient(
|
|
painter: &egui::Painter,
|
|
rect: egui::Rect,
|
|
angle_degrees: f32,
|
|
stops: &[(f32, egui::Color32)],
|
|
rounding: f32,
|
|
) {
|
|
if stops.len() < 2 {
|
|
if let Some((_, color)) = stops.first() {
|
|
painter.rect_filled(rect, rounding, *color);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Convert CSS angle to a direction vector
|
|
// CSS: 0deg = to top, 90deg = to right, 180deg = to bottom
|
|
let angle_rad = (angle_degrees - 90.0).to_radians();
|
|
let dir = egui::vec2(angle_rad.cos(), angle_rad.sin());
|
|
|
|
// Project rect corners onto gradient direction to find start/end
|
|
let center = rect.center();
|
|
let half_size = rect.size() / 2.0;
|
|
|
|
// The gradient line length is the projection of the rect diagonal onto the direction
|
|
let gradient_half_len = (half_size.x * dir.x.abs()) + (half_size.y * dir.y.abs());
|
|
|
|
// For simple horizontal/vertical gradients with no rounding, use a mesh directly
|
|
if rounding <= 0.0 {
|
|
let mut mesh = egui::Mesh::default();
|
|
mesh.texture_id = egui::TextureId::default();
|
|
|
|
// For each consecutive pair of stops, add a quad
|
|
for i in 0..stops.len() - 1 {
|
|
let (t0, c0) = stops[i];
|
|
let (t1, c1) = stops[i + 1];
|
|
|
|
// Map t to positions along the gradient line
|
|
let p0_along = -gradient_half_len + t0 * 2.0 * gradient_half_len;
|
|
let p1_along = -gradient_half_len + t1 * 2.0 * gradient_half_len;
|
|
|
|
// Perpendicular direction for quad width
|
|
let perp = egui::vec2(-dir.y, dir.x);
|
|
let perp_extent = (half_size.x * perp.x.abs()) + (half_size.y * perp.y.abs());
|
|
|
|
let base0 = center + dir * p0_along;
|
|
let base1 = center + dir * p1_along;
|
|
|
|
let v0 = base0 - perp * perp_extent;
|
|
let v1 = base0 + perp * perp_extent;
|
|
let v2 = base1 + perp * perp_extent;
|
|
let v3 = base1 - perp * perp_extent;
|
|
|
|
let uv = egui::pos2(0.0, 0.0);
|
|
let idx = mesh.vertices.len() as u32;
|
|
mesh.vertices.push(egui::epaint::Vertex { pos: v0, uv, color: c0 });
|
|
mesh.vertices.push(egui::epaint::Vertex { pos: v1, uv, color: c0 });
|
|
mesh.vertices.push(egui::epaint::Vertex { pos: v2, uv, color: c1 });
|
|
mesh.vertices.push(egui::epaint::Vertex { pos: v3, uv, color: c1 });
|
|
|
|
mesh.indices.extend_from_slice(&[idx, idx + 1, idx + 2, idx, idx + 2, idx + 3]);
|
|
}
|
|
|
|
painter.add(egui::Shape::mesh(mesh));
|
|
} else {
|
|
// For rounded rects, paint without rounding for now.
|
|
// TODO: proper rounded gradient with tessellation or clip mask
|
|
paint_linear_gradient(painter, rect, angle_degrees, stops, 0.0);
|
|
}
|
|
}
|