//! Avatar sizing-checkpoint harness: renders the REAL `avatar_tex` (custom //! image, no ring) and `gradient_avatar` across every size the app uses. //! Names never affect the avatar — this just checks sizing by eye. //! Run: `cargo run --example avatar_ring` (screenshots taken externally). use eframe::egui; use grim::gui::views::goblin::widgets as w; const SIZES: [f32; 6] = [28.0, 40.0, 48.0, 56.0, 72.0, 96.0]; const NAMES: [&str; 3] = ["alice", "bob", "carmen"]; struct App { tex: Vec, } /// A synthetic "profile photo": diagonal two-tone blend with a light disc, so /// sizing is judged against something photo-like rather than a flat fill. fn photo(ctx: &egui::Context, name: &str, a: [u8; 3], b: [u8; 3]) -> egui::TextureHandle { const N: usize = 128; let mut px = Vec::with_capacity(N * N); for y in 0..N { for x in 0..N { let t = (x + y) as f32 / (2 * N) as f32; let mut r = a[0] as f32 * (1.0 - t) + b[0] as f32 * t; let mut g = a[1] as f32 * (1.0 - t) + b[1] as f32 * t; let mut bl = a[2] as f32 * (1.0 - t) + b[2] as f32 * t; let dx = x as f32 - 44.0; let dy = y as f32 - 40.0; if (dx * dx + dy * dy).sqrt() < 26.0 { r = (r + 90.0).min(255.0); g = (g + 90.0).min(255.0); bl = (bl + 90.0).min(255.0); } px.push(egui::Color32::from_rgb(r as u8, g as u8, bl as u8)); } } let img = egui::ColorImage { size: [N, N], source_size: egui::Vec2::splat(N as f32), pixels: px, }; ctx.load_texture(name.to_string(), img, Default::default()) } impl App { fn new(cc: &eframe::CreationContext) -> Self { egui_extras::install_image_loaders(&cc.egui_ctx); let tex = vec![ photo(&cc.egui_ctx, "alice", [180, 120, 90], [90, 60, 120]), photo(&cc.egui_ctx, "bob", [70, 110, 160], [40, 160, 120]), photo(&cc.egui_ctx, "carmen", [160, 70, 90], [220, 170, 80]), ]; Self { tex } } } impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _f: &mut eframe::Frame) { egui::CentralPanel::default() .frame(egui::Frame::default().fill(egui::Color32::from_rgb(0xFA, 0xFA, 0xF7))) .show(ctx, |ui| { ui.add_space(10.0); ui.heading("avatar sizing sheet (no ring — names never affect the avatar)"); ui.add_space(12.0); for (i, name) in NAMES.iter().enumerate() { ui.horizontal(|ui| { ui.add_space(12.0); ui.label(format!("{name:>7}")); for size in SIZES { ui.add_space(14.0); w::avatar_tex(ui, &self.tex[i], name, size); } }); ui.add_space(14.0); } ui.separator(); ui.label("anonymous npub (grinmark gradient):"); ui.add_space(8.0); ui.horizontal(|ui| { ui.add_space(12.0); ui.label(" "); for (i, size) in SIZES.iter().enumerate() { ui.add_space(14.0); w::gradient_avatar(ui, &format!("{i}deadbeef{i}"), *size); } }); ui.add_space(10.0); ui.horizontal(|ui| { ui.add_space(12.0); ui.label("sizes: "); for size in SIZES { ui.add_space(14.0); ui.allocate_ui(egui::Vec2::new(size, 16.0), |ui| { ui.centered_and_justified(|ui| ui.small(format!("{size}"))); }); } }); }); } } fn main() -> eframe::Result { let opts = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([900.0, 640.0]), ..Default::default() }; eframe::run_native( "avatar-ring", opts, Box::new(|cc| Ok(Box::new(App::new(cc)))), ) }