From 9c6dbfdfab528d16e4c9b02296826209991a654a Mon Sep 17 00:00:00 2001 From: prxnvt <53838599+prxnvt@users.noreply.github.com> Date: Mon, 2 Nov 2026 10:43:28 -0500 Subject: [PATCH] UI: the code field draws the equation editor's error mark again The merge of main brought #471's code_editor_marked, whose mark was drawn by the layouter inside the code editor; on this branch that layouter lives in code_field, which had no mark, so the merge did not compile. FieldOptions gains mark (None by default), code_editor_marked passes it, and code_field underlines it as before. Test: the_marked_editor_underlines_its_mark (fails when the field ignores the mark). --- crates/ui-egui/src/code_editor.rs | 7 +++++-- crates/ui-egui/src/code_editor/tests.rs | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/code_editor.rs b/crates/ui-egui/src/code_editor.rs index 67167a8b9..589c22e9a 100644 --- a/crates/ui-egui/src/code_editor.rs +++ b/crates/ui-egui/src/code_editor.rs @@ -583,7 +583,7 @@ pub fn code_editor_marked(ui: &mut Ui, id: egui::Id, text: &mut String, lang: La let max_lines = max_lines.clamp(1, 200); let mut response = None; egui::Frame::new().fill(t.input_bg).stroke(egui::Stroke::new(1.0, t.input_border)).corner_radius(4.0).inner_margin(4.0).show(ui, |ui| { - let options = FieldOptions { max_height: row_h * max_lines as f32, ..FieldOptions::new(lang) }; + let options = FieldOptions { max_height: row_h * max_lines as f32, mark: mark.clone(), ..FieldOptions::new(lang) }; response = Some(code_field(ui, id, text, &options).response); }); response.unwrap_or_else(|| ui.allocate_response(egui::Vec2::ZERO, egui::Sense::hover())) @@ -606,6 +606,8 @@ pub struct FieldOptions<'a> { pub caret: Option, /// The field's name for screen readers. pub name: &'a str, + /// A byte range underlined in the error colour (see [`underline`]). + pub mark: Option>, } impl FieldOptions<'_> { @@ -621,6 +623,7 @@ impl FieldOptions<'_> { // The editor is a generic code field, so the name is not part of the UI's translated // strings unless the caller gives one. name: "Code Editor", + mark: None, } } } @@ -685,7 +688,7 @@ pub fn code_field(ui: &mut Ui, id: egui::Id, text: &mut String, options: &FieldO let danger = t.danger; let mut layouter = |ui: &Ui, buf: &dyn egui::TextBuffer, wrap_width: f32| { let mut job = layout_job(buf.as_str(), lang, &self::font(), plain, &syntax, wrap_width); - if let Some(m) = mark.clone() { + if let Some(m) = options.mark.clone() { underline(&mut job, m, danger); } ui.fonts_mut(|f| f.layout_job(job)) diff --git a/crates/ui-egui/src/code_editor/tests.rs b/crates/ui-egui/src/code_editor/tests.rs index 4cd93bd1b..b3393aec5 100644 --- a/crates/ui-egui/src/code_editor/tests.rs +++ b/crates/ui-egui/src/code_editor/tests.rs @@ -459,3 +459,24 @@ fn a_repeat_pass_of_the_frame_counts_as_the_next_pass() { h.run_steps(1); assert_eq!(h.state().0, " a\n b"); } + +/// Whether a frame drew text with an underline. +fn draws_underline(mark: Option>) -> bool { + let ctx = egui::Context::default(); + let mut out = ctx.run_ui(egui::RawInput::default(), |ui| { + let mut text = String::from("x^{2"); + code_editor_marked(ui, egui::Id::unique("marked"), &mut text, Lang::Latex, 3, mark.clone()); + }); + out.textures_delta.clear(); + out.shapes.iter().any(|s| match &s.shape { + egui::Shape::Text(t) => t.galley.job.sections.iter().any(|sec| sec.format.underline.width > 0.0), + _ => false, + }) +} + +#[test] +fn the_marked_editor_underlines_its_mark() { + // The equation editor marks an error's position; the code field draws it. + assert!(draws_underline(Some(3..4))); + assert!(!draws_underline(None)); +}