pub(crate) mod frame;
pub(crate) use frame::paint;
pub(crate) type Painted = (String, egui::Rect);
#[derive(Clone, Debug)]
pub(crate) struct Seen {
pub(crate) text: String,
pub(crate) laid: egui::Rect,
pub(crate) shown: egui::Rect,
pub(crate) ink: egui::Color32,
}
fn descend(shape: &egui::Shape, out: &mut Vec<(String, egui::Rect, egui::Color32)>) {
match shape {
egui::Shape::Text(t) => out.push((
visible(&t.galley),
t.galley.rect.translate(t.pos.to_vec2()),
ink(t),
)),
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| descend(s, out)),
_ => {}
}
}
fn ink(t: &egui::epaint::TextShape) -> egui::Color32 {
match t.galley.job.sections.first().map(|s| s.format.color) {
Some(colour) if colour != egui::Color32::PLACEHOLDER => colour,
_ => t.fallback_color,
}
}
fn visible(galley: &egui::Galley) -> String {
let mut out = String::new();
for row in &galley.rows {
out.extend(row.glyphs.iter().map(|g| g.chr));
if row.ends_with_newline {
out.push('\n');
}
}
out
}
pub(crate) fn collect(shape: &egui::Shape, out: &mut Vec<Painted>) {
let mut inked = Vec::new();
descend(shape, &mut inked);
out.extend(inked.into_iter().map(|(text, rect, _)| (text, rect)));
}
pub(crate) fn collect_text(shape: &egui::Shape, out: &mut String) {
let mut painted = Vec::new();
collect(shape, &mut painted);
for (text, _) in painted {
out.push_str(&text);
out.push('\n');
}
}
pub(crate) fn text_of(output: &egui::FullOutput) -> String {
let mut text = String::new();
for clipped in &output.shapes {
collect_text(&clipped.shape, &mut text);
}
text
}
pub(crate) fn seen_of(output: &egui::FullOutput) -> Vec<Seen> {
let mut out = Vec::new();
for clipped in &output.shapes {
let mut here = Vec::new();
descend(&clipped.shape, &mut here);
out.extend(here.into_iter().filter_map(|(text, laid, ink)| {
let shown = laid.intersect(clipped.clip_rect);
(shown.width() > 0.5 && shown.height() > 0.5).then_some(Seen {
text,
laid,
shown,
ink,
})
}));
}
out
}
pub(crate) fn locate(output: &egui::FullOutput, text: &str) -> Option<egui::Pos2> {
seen_of(output)
.into_iter()
.find(|seen| seen.text == text)
.map(|seen| seen.shown.center())
}
#[cfg(test)]
mod tests;