use gled_protocol::assets::KnownAsset;
use std::sync::Arc;
use egui::{
Color32, Context, Id, Mesh, Rect, Shape, TextureHandle, Vec2,
epaint::{Vertex, WHITE_UV},
};
use std::path::Path;
use crate::canvas::images::Images;
use crate::canvas::view::fit_into;
use crate::ui::{color::ToColor32, curve_editor::CurveEditor, pills::show_pills};
use gled_model::asset::{
animation::Animation, curve::Curve, midi_controller::MidiController,
output_device::OutputDevice, palette::Palette, project::Project, scene::Scene,
};
use gled_model::patching::definition::FixtureDefinition;
pub(crate) trait AssetUi: KnownAsset {
fn show(&self, ui: &mut egui::Ui, rect: Rect) {
let _ = (ui, rect);
}
}
impl AssetUi for Animation {}
impl AssetUi for MidiController {}
impl AssetUi for OutputDevice {}
impl AssetUi for Project {}
impl AssetUi for FixtureDefinition {
fn show(&self, ui: &mut egui::Ui, rect: Rect) {
const WIDTH: f32 = 64.0;
const DOT: f32 = 1.2;
let area =
Rect::from_min_max(egui::pos2(rect.max.x - WIDTH, rect.min.y), rect.max).shrink(1.0);
if let Some(texture) = self
.photo
.as_ref()
.and_then(|photo| list_photo(ui.ctx(), &photo.file))
{
ui.painter_at(area).image(
texture.id(),
fit_into(texture.size_vec2(), area),
Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)),
Color32::WHITE,
);
return;
}
let Some((min, max)) = self.pixel_bounds_m() else {
return;
};
let extent = Vec2::new(max[0] - min[0], max[1] - min[1]);
let scale = (area.width() / extent.x.max(f32::EPSILON))
.min(area.height() / extent.y.max(f32::EPSILON));
let centre = egui::pos2((min[0] + max[0]) / 2.0, (min[1] + max[1]) / 2.0);
let painter = ui.painter_at(area);
for pixel in &self.pixels {
let offset = Vec2::new(
pixel.position_m[0] - centre.x,
pixel.position_m[1] - centre.y,
);
painter.circle_filled(area.center() + offset * scale, DOT, Color32::from_gray(200));
}
}
}
fn list_photo(ctx: &Context, file: &Path) -> Option<TextureHandle> {
let id = Id::new("fixture definition list photos");
let mut images = ctx
.data_mut(|data| data.remove_temp::<Images>(id))
.unwrap_or_default();
let texture = images.texture(ctx, file);
ctx.data_mut(|data| data.insert_temp(id, images));
texture
}
impl AssetUi for Curve {
fn show(&self, ui: &mut egui::Ui, rect: Rect) {
let mut min = rect.min;
min.x = rect.max.x - 64.0;
let rect = Rect::from_min_max(min, rect.max);
let mut curve = self.clone();
curve.draw_curve(ui, false, None, rect);
}
}
impl AssetUi for Palette {
fn show(&self, ui: &mut egui::Ui, rect: Rect) {
let mesh = {
const COLOR_GRADIENT_RATIO: f32 = 0.35;
const GAP: f32 = 2.0;
const BORDER: f32 = 1.0;
const HEIGHT: f32 = 18.0;
let available_width = rect.width() - GAP - 4.0 * BORDER;
let color_width = available_width * COLOR_GRADIENT_RATIO;
let gradient_with: f32 = available_width - color_width;
let width_per_gradient_color = gradient_with / 16.0;
let start_pos = rect.right_center();
let mut mesh = Mesh::default();
mesh.add_colored_rect(
Rect::from_min_max(
start_pos
- Vec2::new(
BORDER + color_width + BORDER + GAP + BORDER + gradient_with + BORDER,
HEIGHT / 2.0,
),
start_pos - Vec2::new(GAP + BORDER + gradient_with + BORDER, -HEIGHT / 2.0),
),
Color32::BLACK,
);
let index = mesh.vertices.len() as u32;
mesh.add_triangle(index, index + 1, index + 2);
mesh.add_triangle(index + 3, index + 4, index + 5);
let primary_color = self.primary.color32();
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
color_width + BORDER + GAP + BORDER + gradient_with + BORDER,
HEIGHT / 2.0 - BORDER,
),
uv: WHITE_UV,
color: primary_color,
});
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
BORDER + GAP + BORDER + gradient_with + BORDER,
HEIGHT / 2.0 - BORDER,
),
uv: WHITE_UV,
color: primary_color,
});
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
color_width + BORDER + GAP + BORDER + gradient_with + BORDER,
-HEIGHT / 2.0 + BORDER,
),
uv: WHITE_UV,
color: primary_color,
});
let secondary_color = self.secondary.color32();
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
BORDER + GAP + BORDER + gradient_with + BORDER,
HEIGHT / 2.0 - BORDER,
),
uv: WHITE_UV,
color: secondary_color,
});
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
color_width + BORDER + GAP + BORDER + gradient_with + BORDER,
-HEIGHT / 2.0 + BORDER,
),
uv: WHITE_UV,
color: secondary_color,
});
mesh.vertices.push(Vertex {
pos: start_pos
- Vec2::new(
BORDER + GAP + BORDER + gradient_with + BORDER,
-HEIGHT / 2.0 + BORDER,
),
uv: WHITE_UV,
color: secondary_color,
});
mesh.add_colored_rect(
Rect::from_min_max(
start_pos - Vec2::new(BORDER + gradient_with + BORDER, HEIGHT / 2.0),
start_pos - Vec2::new(BORDER, -HEIGHT / 2.0),
),
Color32::BLACK,
);
for (i, color) in self.gradient.into_iter().rev().enumerate() {
mesh.add_colored_rect(
Rect::from_min_max(
start_pos
- Vec2::new(
width_per_gradient_color * (i + 1) as f32 + BORDER,
HEIGHT / 2.0 - BORDER,
),
start_pos
- Vec2::new(
width_per_gradient_color * i as f32 + BORDER,
-HEIGHT / 2.0 + BORDER,
),
),
color.color32(),
);
}
mesh
};
ui.painter().add(Shape::Mesh(Arc::new(mesh)));
}
}
impl AssetUi for Scene {
fn show(&self, ui: &mut egui::Ui, rect: egui::Rect) {
show_pills(
ui,
rect.right_top() + Vec2::new(0.0, 1.0),
self.selections()
.into_iter()
.map(|selection| (selection.mode.to_string(), Color32::GOLD))
.collect(),
);
}
}