//! Non-interactive Penpot v2.3.1 renderer-control preview. use egui; use egui_phosphor_icons::icons; use crate::design_system::typography::TypeRole; use crate::design_system::{self, ADVANCED_COLLAPSED_HEIGHT, ADVANCED_EXPANDED_HEIGHT}; const PLANNED_TOOLTIP: &str = "Planned — renderer support pending"; pub(super) fn unsupported_advanced_preview(ui: &mut egui::Ui) { let open_id = ui.make_persistent_id("material_unsupported_advanced_open"); let reveal_id = open_id.with("reveal_complete_body"); let mut open = ui .ctx() .data_mut(|data| data.get_persisted::(open_id)) .unwrap_or(false); let width = ui.available_width().max(1.0); let mode = super::layout::ResponsiveMaterialLayout::for_width(width); let height = if open { if mode == super::layout::ResponsiveMaterialLayout::Transient { 254.0 } else { ADVANCED_EXPANDED_HEIGHT } } else { ADVANCED_COLLAPSED_HEIGHT }; let palette = design_system::palette(ui); let (rect, _) = ui.allocate_exact_size(egui::vec2(width, height), egui::Sense::hover()); ui.painter().rect( rect, 5.0, palette.panel, egui::Stroke::new(1.0_f32, palette.border), egui::StrokeKind::Inside, ); let header = egui::Rect::from_min_size(rect.min, egui::vec2(rect.width(), 30.0)); ui.painter().rect_filled(header, 4.0, palette.panel); let header_response = ui.interact(header, open_id.with("header"), egui::Sense::click()); if header_response.clicked() { open = !open; ui.ctx().data_mut(|data| data.insert_temp(reveal_id, open)); } header_response.on_hover_text(if open { "Collapse Advanced preview" } else { "Expand Advanced preview" }); ui.painter().text( header.left_center() + egui::vec2(12.0, 0.0), egui::Align2::LEFT_CENTER, if open { icons::CARET_DOWN.as_str() } else { icons::CARET_RIGHT.as_str() }, egui::FontId::new(10.0, egui::FontFamily::Name("phosphor-bold".into())), palette.text_secondary, ); ui.painter().text( header.left_center() + egui::vec2(26.0, 0.0), egui::Align2::LEFT_CENTER, "Advanced", TypeRole::Section.font(), palette.text_secondary, ); if !open && rect.width() >= 300.0 { ui.painter().hline( (header.left() + 82.0)..=(header.right() - 158.0), header.center().y, egui::Stroke::new(1.0_f32, palette.border), ); ui.painter().text( header.right_center() - egui::vec2(12.0, 0.0), egui::Align2::RIGHT_CENTER, PLANNED_TOOLTIP, TypeRole::Small.font(), palette.text_muted, ); } if open { match mode { super::layout::ResponsiveMaterialLayout::Reference => reference_controls(ui, rect), super::layout::ResponsiveMaterialLayout::Compact => { compact_reference_controls(ui, rect) } super::layout::ResponsiveMaterialLayout::Transient => compact_controls(ui, rect), } } let reveal_complete_body = ui .ctx() .data_mut(|data| data.get_temp::(reveal_id)) .unwrap_or(false); if open && reveal_complete_body && rect.height() > ADVANCED_COLLAPSED_HEIGHT { ui.scroll_to_rect(rect, Some(egui::Align::BOTTOM)); ui.ctx().data_mut(|data| data.remove::(reveal_id)); } ui.ctx() .data_mut(|data| data.insert_persisted(open_id, open)); } fn reference_controls(ui: &mut egui::Ui, rect: egui::Rect) { disabled_field(ui, rect, 28.0, 42.0, "Culling", "Back"); disabled_field(ui, rect, 28.0, 76.0, "Render Queue", "From Shader"); disabled_toggle(ui, rect, 328.0, 42.0, "Depth Write", true); disabled_toggle(ui, rect, 328.0, 76.0, "Receive Shadows", true); planned_note(ui, rect, 28.0, 124.0); } fn compact_reference_controls(ui: &mut egui::Ui, rect: egui::Rect) { disabled_field_at(ui, rect, 20.0, 90.0, 90.0, 42.0, "Culling", "Back"); disabled_field_at( ui, rect, 20.0, 90.0, 90.0, 76.0, "Render Queue", "From Shader", ); disabled_toggle_at( ui, rect, 196.0, rect.width() - 38.0, 42.0, "Depth Write", true, ); disabled_toggle_at( ui, rect, 196.0, rect.width() - 38.0, 76.0, "Receive Shadows", true, ); planned_note(ui, rect, 20.0, 124.0); } fn compact_controls(ui: &mut egui::Ui, rect: egui::Rect) { disabled_field(ui, rect, 20.0, 42.0, "Culling", "Back"); disabled_field(ui, rect, 20.0, 78.0, "Render Queue", "From Shader"); disabled_toggle(ui, rect, 20.0, 124.0, "Depth Write", true); disabled_toggle(ui, rect, 20.0, 160.0, "Receive Shadows", true); planned_note(ui, rect, 20.0, 210.0); } fn disabled_field(ui: &mut egui::Ui, rect: egui::Rect, x: f32, y: f32, label: &str, value: &str) { disabled_field_at(ui, rect, x, x + 106.0, 150.0, y, label, value); } #[expect( clippy::too_many_arguments, reason = "explicit Penpot preview geometry" )] fn disabled_field_at( ui: &mut egui::Ui, rect: egui::Rect, label_x: f32, field_x: f32, field_width: f32, y: f32, label: &str, value: &str, ) { let palette = design_system::palette(ui); ui.painter().text( rect.min + egui::vec2(label_x, y), egui::Align2::LEFT_CENTER, label, TypeRole::Body.font(), palette.text_muted, ); let field = egui::Rect::from_min_size( egui::pos2(rect.left() + field_x, rect.top() + y - 7.0), egui::vec2(field_width.min(rect.width() - field_x - 8.0).max(1.0), 24.0), ); paint_disabled_field(ui, field, value); } fn disabled_toggle( ui: &mut egui::Ui, rect: egui::Rect, x: f32, y: f32, label: &str, enabled: bool, ) { disabled_toggle_at(ui, rect, x, x + 106.0, y, label, enabled); } fn disabled_toggle_at( ui: &mut egui::Ui, rect: egui::Rect, label_x: f32, toggle_x: f32, y: f32, label: &str, enabled: bool, ) { let palette = design_system::palette(ui); ui.painter().text( rect.min + egui::vec2(label_x, y), egui::Align2::LEFT_CENTER, label, TypeRole::Body.font(), palette.text_muted, ); let toggle = egui::Rect::from_min_size( egui::pos2(rect.left() + toggle_x, rect.top() + y - 8.0), egui::vec2(30.0, 16.0), ); ui.painter().rect( toggle, 8.0, palette.control, egui::Stroke::new(1.0_f32, palette.border), egui::StrokeKind::Inside, ); let thumb_x = if enabled { toggle.right() - 8.0 } else { toggle.left() + 8.0 }; ui.painter().circle_filled( egui::pos2(thumb_x, toggle.center().y), 5.0, palette.text_muted, ); ui.interact( toggle, ui.make_persistent_id(("advanced_toggle", label)), egui::Sense::hover(), ) .on_hover_text(PLANNED_TOOLTIP); } fn paint_disabled_field(ui: &mut egui::Ui, rect: egui::Rect, value: &str) { let palette = design_system::palette(ui); ui.painter().rect( rect, 4.0, palette.control, egui::Stroke::new(1.0_f32, palette.border), egui::StrokeKind::Inside, ); ui.painter().text( rect.left_center() + egui::vec2(10.0, 0.0), egui::Align2::LEFT_CENTER, value, TypeRole::Body.font(), palette.text_muted, ); ui.interact( rect, ui.make_persistent_id(("advanced_field", value)), egui::Sense::hover(), ) .on_hover_text(PLANNED_TOOLTIP); } fn planned_note(ui: &egui::Ui, rect: egui::Rect, x: f32, y: f32) { let palette = design_system::palette(ui); ui.painter().text( rect.min + egui::vec2(x, y), egui::Align2::LEFT_CENTER, PLANNED_TOOLTIP, TypeRole::Small.font(), palette.text_muted, ); } #[cfg(test)] mod tests { use super::*; #[test] fn penpot_advanced_preview_matches_v231_heights() { assert_eq!(ADVANCED_COLLAPSED_HEIGHT, 30.0); assert_eq!(ADVANCED_EXPANDED_HEIGHT, 178.0); } }