use super::{Kind, Row, trail};
use crate::ui::theme::{self, Speaker, glyph};
pub const WORKED: &str = "worked";
pub fn counted(steps: &[Row]) -> String {
let calls = steps.iter().filter(|row| is_call(row)).count();
let notes = steps.len() - calls;
let sep = glyph::NOTE;
format!(
"{WORKED} {sep} {} {sep} {}",
crate::render::counted(calls as u64, "call"),
crate::render::counted(notes as u64, "note"),
)
}
fn is_call(row: &Row) -> bool {
matches!(row.kind, Kind::Call | Kind::Answered | Kind::Result)
}
fn in_flight(row: &Row, last: bool) -> bool {
row.kind == Kind::Call || (row.live && last)
}
pub(super) fn working(ui: &mut egui::Ui, at: usize, steps: &[Row]) -> bool {
let id = ui.make_persistent_id(("working", at));
let live = steps.iter().any(|row| row.live);
let mut open = ui.data(|d| d.get_temp::<bool>(id)).unwrap_or(live);
let head = format!(
"{} {}",
if open { glyph::OPEN } else { glyph::SHUT },
counted(steps)
);
if theme::paint::tab(ui, &head, theme::INK_WEAK).clicked() {
open = !open;
ui.data_mut(|d| d.insert_temp(id, open));
}
if !open {
return false;
}
let x = ui.cursor().min.x + theme::space::S;
for (i, row) in steps.iter().enumerate() {
let top = ui.cursor().min.y;
let last = i + 1 == steps.len();
ui.horizontal_top(|ui| {
ui.add_space(theme::space::L);
if in_flight(row, last) {
trail::step(ui, at + i, row, false);
} else {
finished(ui, at + i, row);
}
});
let bottom = ui.min_rect().max.y;
let middle = top + theme::space::S;
theme::paint::elbow(ui, x, top, middle, x + theme::space::S);
if !last {
theme::paint::rail(ui, x, middle, bottom);
}
}
true
}
fn finished(ui: &mut egui::Ui, at: usize, row: &Row) {
let id = ui.make_persistent_id(("step", at));
let open = ui.data(|d| d.get_temp::<bool>(id)).unwrap_or(false);
let toggled = if open {
trail::step(ui, at, row, true)
} else {
compact(ui, row).clicked()
};
if toggled {
ui.data_mut(|d| d.insert_temp(id, !open));
}
}
fn compact(ui: &mut egui::Ui, row: &Row) -> egui::Response {
let red = theme::accent(theme::State::Error);
let first = row.said.lines().next().unwrap_or_default();
let weak = theme::INK_WEAK;
let (mark, words, beneath) = if is_call(row) {
let mark = if row.failed {
(glyph::FAILED, red)
} else {
(glyph::DONE, weak)
};
let beneath = if first.is_empty() {
Vec::new()
} else {
vec![(first.to_owned(), weak, egui::TextStyle::Body)]
};
(mark, row.who.clone(), beneath)
} else {
let mark = if row.weight == Speaker::Model && row.kind == Kind::Said {
glyph::MODEL
} else {
glyph::NOTE
};
let words = format!("{} {} {first}", row.who, glyph::NOTE);
((mark, weak), words, Vec::new())
};
theme::paint::Row {
mark: (mark.0.to_owned(), mark.1),
words,
ink: if row.failed { red } else { weak },
state: row.failed.then_some(red),
chosen: false,
indent: 0.0,
aside: None,
beneath,
}
.paint(ui)
}