1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//! Ball-badge painters for the conversation surfaces (§3.5, §11, §15 Z9), split
//! from [`super::navigator`] per §12's 300-line budget. Coverage-excluded glue:
//! the ids, states, and badges come from the tested `nav::convs`/`AppModel`
//! derivations; these only choose the hue (`theme::ball_hue`) and lay the widgets.
use crate::nav::convs::ConvBall;
use crate::nav::convs::group::ConvGroup;
use crate::theme;
/// A conversation row's ball-id badge (§3.5): the id from the goal stamp (source
/// 1), coloured by the derived join status when known, neutral otherwise.
pub(super) fn row_badge(ui: &mut egui::Ui, ball: &ConvBall) {
let label = format!("◈{}", ball.id);
match ball.state {
Some(s) => ui.colored_label(theme::ball_hue(s), label),
None => ui.weak(label),
};
}
/// The grouped view's ball header: the ball id in its §3.5 join hue plus its
/// badge, or "unassociated" for the trailing group (§11 grouped view).
pub(super) fn group_header(ui: &mut egui::Ui, group: &ConvGroup) {
let Some(ball) = &group.ball else {
ui.weak("◈ unassociated");
return;
};
let label = format!("◈ {}", ball.id);
// Badge pinned right, id truncating into what is left — the roster's
// no-overflow rule (bl-9669: an overflowing row ratchets the panel wider).
ui.horizontal(|ui| {
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if let Some(badge) = &ball.badge {
ui.weak(badge);
}
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
match ball.state {
Some(s) => ui.colored_label(theme::ball_hue(s), label),
// An unresolved stamp still names its ball, neutral hue.
None => ui.weak(label),
};
});
});
});
}
/// The conversation-header ball (§3.3): the id, its title, status hue, and badge
/// — the conversation's own start-flow ball, one per conversation (§3.2). `None`
/// leaves the header ball-less.
pub(super) fn header_ball(ui: &mut egui::Ui, ball: &ConvBall) {
let label = match &ball.title {
Some(t) => format!("◈ {} · {t}", ball.id),
None => format!("◈ {}", ball.id),
};
ui.horizontal(|ui| {
match ball.state {
Some(s) => ui.colored_label(theme::ball_hue(s), label),
None => ui.weak(label),
};
if let Some(badge) = &ball.badge {
ui.weak(badge);
}
});
}