use std::fmt::Write as _;
use egui::{Context, Id, IdMap, LayerId, Order, Rect};
use super::{GRAPH_ID, Key0, NavGraph, NavKind};
#[derive(Clone, Debug)]
pub(super) struct Harvested {
id: Id,
parent_ui: Id,
layer: LayerId,
rect: Rect,
label: String,
dest: u32,
}
#[derive(Clone, Default)]
pub(super) struct HarvestBuf {
items: Vec<Harvested>,
len: usize,
by_widget: IdMap<()>,
groups: IdMap<u32>,
key: String,
}
pub const ROOT: &str = "egui";
impl NavGraph {
pub(super) fn harvest(&mut self, ctx: &Context) {
let mut buf = std::mem::take(&mut self.harvest);
buf.len = 0;
buf.by_widget.clear();
buf.groups.clear();
for n in self.nodes() {
if let Some(w) = n.widget {
buf.by_widget.insert(w, ());
}
}
let own = LayerId::new(Order::Debug, Id::new(GRAPH_ID));
ctx.viewport(|v| {
for (layer, rects) in v.this_pass.widgets.layers() {
if *layer == own || matches!(layer.order, Order::Debug | Order::Tooltip) {
continue;
}
for r in rects {
let navigable = r.sense.senses_click() || r.sense.is_focusable();
if !navigable || !r.rect.is_positive() || buf.by_widget.contains_key(&r.id) {
continue;
}
if buf.len == buf.items.len() {
buf.items.push(Harvested {
id: r.id,
parent_ui: r.parent_id,
layer: *layer,
rect: r.rect,
label: String::new(),
dest: 0,
});
} else {
let h = &mut buf.items[buf.len];
h.id = r.id;
h.parent_ui = r.parent_id;
h.layer = *layer;
h.rect = r.rect;
h.label.clear();
}
buf.len += 1;
}
}
});
if buf.len == 0 {
self.harvest = buf;
return;
}
for h in &mut buf.items[..buf.len] {
let label = &mut h.label;
ctx.accesskit_node_builder(h.id, |node| {
if let Some(l) = node.label() {
label.push_str(l);
}
});
}
let root = match self.nodes().iter().position(|n| n.parent.is_none()) {
Some(i) => i as u32,
None => match self.register(Key0::Str(ROOT), ROOT, NavKind::App, ctx.content_rect(), None, None) {
Some(i) => {
self.nodes[i as usize].harvested = true;
i
}
None => {
self.harvest = buf;
return;
}
},
};
let registered = self.len;
for h in &mut buf.items[..buf.len] {
h.dest = if h.layer == LayerId::background() {
self.deepest_scope(h.rect.center(), registered).unwrap_or(root)
} else {
buf.key.clear();
let _ = write!(buf.key, "layer-{:016x}", h.layer.id.value());
match self.register(Key0::Str(&buf.key), "", NavKind::Surface, h.rect, None, Some(root)) {
Some(s) => {
self.nodes[s as usize].harvested = true;
s
}
None => root,
}
};
*buf.groups.entry(h.parent_ui.with(h.dest)).or_insert(0) += 1;
}
for i in 0..buf.len {
let (id, parent_ui, rect, dest) = {
let h = &buf.items[i];
(h.id, h.parent_ui, h.rect, h.dest)
};
let mut parent = dest;
if buf.groups.get(&parent_ui.with(dest)).copied().unwrap_or(0) >= 2 {
buf.key.clear();
let _ = write!(buf.key, "g{:016x}", parent_ui.value());
if let Some(g) = self.register(Key0::Str(&buf.key), "", NavKind::Group, rect, None, Some(dest)) {
self.nodes[g as usize].harvested = true;
parent = g;
}
}
buf.key.clear();
let _ = write!(buf.key, "w{:016x}", id.value());
let label = std::mem::take(&mut buf.items[i].label);
if let Some(n) = self.register(Key0::Str(&buf.key), &label, NavKind::Field, rect, Some(id), Some(parent)) {
self.nodes[n as usize].harvested = true;
}
buf.items[i].label = label;
}
self.harvest = buf;
}
fn deepest_scope(&self, p: egui::Pos2, limit: usize) -> Option<u32> {
let mut best: Option<(u8, u32)> = None;
for (i, n) in self.nodes[..limit].iter().enumerate() {
if n.kind != NavKind::Field && n.rect.contains(p) && best.is_none_or(|(d, _)| n.depth > d) {
best = Some((n.depth, i as u32));
}
}
best.map(|(_, i)| i)
}
}