Fix #45: the Project panel's details stay inside the panel

The details header (item name, size, duration, codec) and the "Select an item" hint were painted
with no width limit, so in a narrow Project panel they ran past its right edge. They now stop
10 px short of it and end in "…" when they don't fit (widgets::text_fit, a shared helper), and
are registered for automation (project.details.*).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bflatasticandClaude Opus 5.5 committed 2026-10-06 00:25:57 +07:00
1 parent 83cd5d0f45
commit e6d4a64775
3 files changed
+40 -3

No files matched your search

+9 -3
View File
@@ -212,8 +212,11 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
},
}
app.auto.add("project.thumbnail", thumb, &it.name);
// The details stay inside the panel (a margin on the right), cut short with "…".
let tx = thumb.max.x + 10.0;
p.text(pos2(tx, thumb.min.y + 6.0), Align2::LEFT_CENTER, &it.name, Tokens::semibold(12.0), t.text);
let max_w = head.max.x - 10.0 - tx;
let r = widgets::text_fit(&p, pos2(tx, thumb.min.y + 6.0), Align2::LEFT_CENTER, &it.name, Tokens::semibold(12.0), max_w, t.text);
app.auto.add("project.details.name", r, &it.name);
let mut lines = vec![];
if let Some((w, h)) = it.dimensions() {
lines.push(format!("{w} x {h} (1.00)"));
@@ -226,10 +229,13 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
lines.push(f.codec.clone());
}
for (i, l) in lines.iter().enumerate() {
p.text(pos2(tx, thumb.min.y + 22.0 + 13.0 * i as f32), Align2::LEFT_CENTER, l, Tokens::ui(11.0), t.text_dim);
let r = widgets::text_fit(&p, pos2(tx, thumb.min.y + 22.0 + 13.0 * i as f32), Align2::LEFT_CENTER, l, Tokens::ui(11.0), max_w, t.text_dim);
app.auto.add(&format!("project.details.{i}"), r, l);
}
} else {
p.text(head.center(), Align2::CENTER_CENTER, "Select an item to see its details", Tokens::ui(11.0), t.text_faint);
let hint = "Select an item to see its details";
let r = widgets::text_fit(&p, head.center(), Align2::CENTER_CENTER, hint, Tokens::ui(11.0), head.width() - 20.0, t.text_faint);
app.auto.add("project.details.hint", r, hint);
}
// Search.
let sr = Rect::from_min_size(pos2(rect.min.x + 8.0, head.max.y + 2.0), vec2(rect.width() - 16.0, 22.0));
+11
View File
@@ -143,6 +143,17 @@ pub fn icon_button(ui: &mut Ui, rect: Rect, icon: Icon, active: bool, t: &Tokens
resp
}
/// One line of `text` at `pos` (placed by `align`), cut short with "…" past `max_w` points so it
/// never runs out of its panel. Returns where it was painted.
pub fn text_fit(p: &egui::Painter, pos: egui::Pos2, align: Align2, text: &str, font: egui::FontId, max_w: f32, color: Color32) -> Rect {
let mut job = egui::text::LayoutJob::simple_singleline(text.to_owned(), font, color);
job.wrap = egui::text::TextWrapping { max_width: max_w.max(0.0), max_rows: 1, break_anywhere: true, overflow_character: Some('…') };
let galley = p.layout_job(job);
let rect = align.anchor_size(pos, galley.size());
p.galley(rect.min, galley, color);
rect
}
/// Rounded search field with a magnifier icon.
pub fn search_field(ui: &mut Ui, rect: Rect, text: &mut String, hint: &str, t: &Tokens) -> Response {
let h = rect.height();
+20
View File
@@ -199,3 +199,23 @@ fn panels_dock_beside_others_and_gutters_resize_them() {
let wider = rect(&h, "panel.Project");
assert!((wider.width() - project.width() - 60.0).abs() < 2.0, "{} → {}", project.width(), wider.width());
}
/// Issue #45: the Project panel's details (name, size, duration) stay inside the panel with a
/// margin, cut short with "…" when they don't fit; so does the hint shown with nothing selected.
#[test]
fn project_details_stay_inside_the_panel() {
let (mut h, pre, _) = harness();
let long = "A composition with a very long name that could never fit beside the thumbnail".to_string();
h.state_mut().session.execute("project.rename", json!({"item": pre, "name": long})).unwrap();
h.state_mut().session.state.project_selection = vec![ItemId(pre)];
h.run_steps(3);
let panel = rect(&h, "panel.Project");
for id in ["project.details.name", "project.details.0", "project.details.1"] {
let r = rect(&h, id);
assert!(r.max.x <= panel.max.x - 9.0 && r.width() > 10.0, "{id}: {r:?} in {panel:?}");
}
h.state_mut().session.state.project_selection.clear();
h.run_steps(3);
let r = rect(&h, "project.details.hint");
assert!(r.min.x >= panel.min.x + 9.0 && r.max.x <= panel.max.x - 9.0, "{r:?} in {panel:?}");
}