use super::*; use crate::{ app::{App, Message}, style, views::{action, short}, }; use iced::{ Element, Fill, widget::{self, column, container, row, text, text_input, tooltip}, }; use std::collections::BTreeSet; const PAGE_SIZE: usize = 6; pub(crate) fn overlay<'a>(app: &'a App, base: Element<'a, Message>) -> Element<'a, Message> { let Some(panel) = &app.diagram_usages else { return base; }; let p = app.project(); let token = panel.token; let message = |event| Message::DiagramUsage(event); let matched = panel.matches(p); let mut surfaces = Vec::new(); let mut seen = BTreeSet::new(); for usage in &matched { if seen.insert(usage.surface) { surfaces.push(usage.surface); } } let targets: Vec<_> = matched .iter() .copied() .filter(|usage| Some(usage.surface) == panel.destination) .collect(); let count = if panel.destination.is_some() { targets.len() } else { surfaces.len() }; let last = count.saturating_sub(1) / PAGE_SIZE; let page = panel.page.min(last); let total_surfaces = panel .usages .iter() .map(|u| u.surface) .collect::>() .len(); let mut content = column![ row![ text("Show in diagrams").size(22).width(Fill), action("Close", message(Event::Close(token))) ] .spacing(12), tooltip( text(format!( "{} · @{}", short(p.named(panel.context.subject), 48), short(alias(p, panel.context.subject), 48) )) .size(14), container( widget::scrollable( text(format!( "{}\n@{}", p.named(panel.context.subject), alias(p, panel.context.subject) )) .size(13) ) .height(220) ) .padding(10) .width(620) .style(style::card), tooltip::Position::Bottom ), text(if panel.loading { "Searching rendered objects…".into() } else { format!( "{} diagram destinations · {} representations", total_surfaces, panel.usages.len() ) }) .size(12) .color(style::MUTED), text_input("Filter diagram, occurrence, or alias", &panel.search) .id(format!("diagram-usage-filter-{}", panel.token)) .padding(9) .on_input(move |value| Message::DiagramUsage(Event::Search(token, value))) .on_submit(message(Event::Activate(token))), ] .spacing(10); if let Some(notice) = &panel.notice { content = content.push(text(notice).size(13)); } if let Some(surface) = panel.destination { content = content.push( row![ action("All diagrams", message(Event::Destination(token, None))), text(short(p.named(surface.id()), 55)).size(15), ] .spacing(10), ); for (index, usage) in targets .into_iter() .enumerate() .skip(page * PAGE_SIZE) .take(PAGE_SIZE) { let name = target_name(p, usage); content = content.push(tooltip( action( &format!("{} · {}", usage.role, short(&name, 70)), message(Event::Choose(token, usage.clone())), ) .width(Fill) .style(if panel.cursor == index { style::selected } else { style::nav }), container(text(name).size(13)) .padding(10) .max_width(680) .style(style::card), tooltip::Position::Top, )); } } else { for (index, surface) in surfaces .into_iter() .enumerate() .skip(page * PAGE_SIZE) .take(PAGE_SIZE) { let targets = matched.iter().filter(|u| u.surface == surface).count(); let name = format!( "{} · {} (@{}) · {} representations", surface.category(p), p.named(surface.id()), alias(p, surface.id()), targets ); let layer = p .diagrams .get(&surface.id()) .map(|d| d.layer) .or_else(|| p.layer_of(surface.id())) .map(|l| l.to_string()) .unwrap_or_default(); content = content.push(tooltip( action( &format!( "{} · {} · {} · {}", surface.category(p), layer, short(p.named(surface.id()), 34), targets ), message(Event::Destination(token, Some(surface))), ) .width(Fill) .style(if panel.cursor == index { style::selected } else { style::nav }), container(text(name).size(13)) .padding(10) .max_width(680) .style(style::card), tooltip::Position::Top, )); } } if panel.loading { content = content.push(text("Finding diagram representations…").size(13)); } else if count == 0 { content = content.push(text(if panel.usages.is_empty() { "No currently rendered representation found. Folded or filtered objects and unexpanded reuse contents are excluded." } else { "No results match this filter." }).size(13)); } let mut paging = row![ text(format!( "Page {} of {} · {} matches", page + 1, last + 1, count )) .size(12) .width(Fill) ] .spacing(8); if page > 0 { paging = paging.push(action("Previous", message(Event::Page(token, page - 1)))); } if page < last { paging = paging.push(action("Next", message(Event::Page(token, page + 1)))); } content = content.push(paging).push(text("↑ ↓ choose · Enter opens · Escape closes. Source and saved view settings are preserved.").size(12).color(style::MUTED)); widget::stack![ base, widget::opaque( container( container(content) .padding(22) .width((app.window_size.width - 80.).clamp(620., 820.)) .style(style::card) ) .center(Fill) .style(|_| container::Style { background: Some(iced::Color::from_rgba(0.08, 0.12, 0.18, 0.38).into()), ..Default::default() }) ) ] .into() }