From 831b8fed43ba9c6cdb605441ab19b76f69c5bee9 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 12:42:05 +0200 Subject: [PATCH] Fix panic drawing a selected guide label in a zero-width canvas Fixes #434 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/guides.rs | 4 ++- crates/ui-egui/tests/guide_label_narrow.rs | 33 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 crates/ui-egui/tests/guide_label_narrow.rs diff --git a/crates/ui-egui/src/guides.rs b/crates/ui-egui/src/guides.rs index 409236f5..b4a7cc0d 100644 --- a/crates/ui-egui/src/guides.rs +++ b/crates/ui-egui/src/guides.rs @@ -316,7 +316,9 @@ fn label(app: &FigApp, painter: &egui::Painter, r: Rect, g: &Placed) { let galley = painter.layout_no_wrap(crate::canvas::fmt(g.offset), semibold(10.0), Color32::WHITE); let size = galley.size() + vec2(8.0, 4.0); let [a, _] = segment(app, g, r); - let at = pos2(a.x.clamp(r.min.x + RULER, r.max.x) + 4.0, a.y.clamp(r.min.y + RULER, r.max.y) + 4.0); + // A canvas narrower or shorter than the ruler leaves `min > max`; keep the tag at the ruler edge then. + let (lo_x, lo_y) = (r.min.x + RULER, r.min.y + RULER); + let at = pos2(a.x.clamp(lo_x, r.max.x.max(lo_x)) + 4.0, a.y.clamp(lo_y, r.max.y.max(lo_y)) + 4.0); let tag = Rect::from_min_size(at, size); painter.rect_filled(tag, CornerRadius::same(3), GUIDE); painter.galley(tag.min + vec2(4.0, 2.0), galley, Color32::WHITE); diff --git a/crates/ui-egui/tests/guide_label_narrow.rs b/crates/ui-egui/tests/guide_label_narrow.rs new file mode 100644 index 00000000..6c4276ad --- /dev/null +++ b/crates/ui-egui/tests/guide_label_narrow.rs @@ -0,0 +1,33 @@ +#![allow(clippy::unwrap_used)] + +use egui::{Pos2, RawInput, Rect, Vec2, vec2}; +use figcraft_engine::Session; +use figcraft_ui_egui::{FigApp, NoServices}; +use serde_json::json; + +fn frame(ctx: &egui::Context, app: &mut FigApp, size: Vec2) { + let mut out = ctx.run_ui(RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, size)), ..Default::default() }, |ui| app.ui(ui)); + out.textures_delta.clear(); +} + +fn scene(selected: bool, rulers: bool) -> (egui::Context, FigApp) { + let mut session = Session::new(); + session.execute("view.rulers", &json!({"on": rulers})).unwrap(); + session.execute("guide.add", &json!({"vertical": true, "offset": 100.0})).unwrap(); + if selected { + session.execute("guide.select", &json!({"index": 0})).unwrap(); + } + let ctx = egui::Context::default(); + let mut app = FigApp::new(session, Box::new(NoServices)); + frame(&ctx, &mut app, vec2(1440.0, 900.0)); + frame(&ctx, &mut app, vec2(1440.0, 900.0)); + (ctx, app) +} + +#[test] +fn selected_guide_frame_completes_in_narrow_layout() { + for (selected, rulers, width) in [(false, true, 200.0), (true, false, 200.0), (true, true, 800.0), (true, true, 200.0)] { + let (ctx, mut app) = scene(selected, rulers); + frame(&ctx, &mut app, vec2(width, 200.0)); + } +}