Merge PR #3862: M7: open the paragraph editor without a fade-in

crates/ui-egui/src/edit_text_ui.rs: #3862's Area builder with fade_in(false), keeping the reveal/look locals and their uses after the Area (#3952, #3863).
crates/ui-egui/tests/editing.rs: kept all new tests.
This commit is contained in:
aiongg committed 2026-10-22 01:46:38 +00:00
commit 2a3482c951
5 files changed
+72 -8

No files matched your search

+3 -1
View File
@@ -523,7 +523,9 @@ pub(crate) fn editor(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, ad
let (mut commit, mut cancel, mut done) = (false, false, false);
let mut undrawable = None;
let memo = &view.content.undrawable;
egui::Area::new(egui::Id::new(("added-text", view.id.0))).order(egui::Order::Foreground).fixed_pos(r.min).constrain(false).show(ctx, |ui| {
// Shown at once, like the paragraph editor: a fade-in shows the page's copy through it.
let area = egui::Area::new(egui::Id::new(("added-text", view.id.0))).fade_in(false);
area.order(egui::Order::Foreground).fixed_pos(r.min).constrain(false).show(ctx, |ui| {
let Some(t) = view.content.draft.as_mut() else { return };
ui.set_width(r.width() + 64.0);
if let Some(source_rect) = source_rect.filter(|_| !background_ready) {
+7 -5
View File
@@ -1531,9 +1531,12 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo) -
let mut done = None;
let mut reveal = false;
let mut look = None;
egui::Area::new(egui::Id::new("edit-text-line")).order(egui::Order::Foreground).fixed_pos(Pos2::new(ed.rect.left(), ed.rect.top())).show(
ctx,
|ui| {
// No fade-in: while it faded, the page's own copy of the text showed through the cover.
egui::Area::new(egui::Id::new("edit-text-line"))
.order(egui::Order::Foreground)
.fade_in(false)
.fixed_pos(Pos2::new(ed.rect.left(), ed.rect.top()))
.show(ctx, |ui| {
// The editor opens at the source size. Once text changes, show the real draft
// extent: single rows grow toward the page edge, paragraphs rewrap at their
// source width, and the visible frame grows/shrinks vertically with their rows.
@@ -1626,8 +1629,7 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo) -
done = Some(true);
}
});
},
);
});
if reveal {
view.text_background.reveal();
}
+3 -1
View File
@@ -706,7 +706,9 @@ pub(crate) fn type_box(ctx: &egui::Context, view: &mut DocView, info: &DocInfo,
let zoom = xf.rect.width() / xf.pw.max(1.0);
let mut commit = false;
let mut cancel = false;
egui::Area::new(egui::Id::new(("fill-text", view.id.0))).order(egui::Order::Foreground).fixed_pos(pos).show(ctx, |ui| {
// Shown at once, like the paragraph editor: a fade-in shows the page's copy through it.
let area = egui::Area::new(egui::Id::new(("fill-text", view.id.0))).fade_in(false);
area.order(egui::Order::Foreground).fixed_pos(pos).show(ctx, |ui| {
let Some(t) = view.fill_text.as_mut() else { return };
// The box is white in every theme, so the caret must not follow the theme (pale in dark mode) (#655).
ui.visuals_mut().text_cursor.stroke.color = crate::edit_text_ui::caret_color_on(Color32::WHITE);
+3 -1
View File
@@ -503,7 +503,9 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, f
match f.kind {
FormFieldKind::Text => {
let multiline = f.has(field_flags::MULTILINE);
egui::Area::new(egui::Id::new(("form-editor", view.id.0))).order(egui::Order::Foreground).fixed_pos(rect.min).show(ctx, |ui| {
// Shown at once, like the paragraph editor: a fade-in shows the field's value through it.
let area = egui::Area::new(egui::Id::new(("form-editor", view.id.0))).fade_in(false);
area.order(egui::Order::Foreground).fixed_pos(rect.min).show(ctx, |ui| {
ui.set_min_size(rect.size());
let Some(fx) = view.forms.focus.as_mut() else { return };
// Sized from the text being typed, so an auto-sized field shrinks to keep it in view.
+56
View File
@@ -2996,6 +2996,62 @@ fn a_paragraph_whose_edit_fails_stays_open_with_its_typing() {
assert_eq!(doc.text_blocks(0).iter().map(|b| b.text.clone()).collect::<Vec<_>>(), ["Top edited", "Bottom"]);
}
/// Two boxes, "Top" and "Bottom", in Edit text, with egui's animations on: the window animates
/// unless the system asks for less motion, and the test harness turns them off.
fn two_boxes_animated() -> (Harness<'static, PdfCraftApp>, [egui::Rect; 2]) {
let bytes = source_font_fixture_with_text("BT /F1 24 Tf 20 220 Td (Top) Tj ET BT /F2 24 Tf 20 120 Td (Bottom) Tj ET");
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).with_step_dt(1.0 / 60.0).build_eframe(move |_cc| {
let mut app = PdfCraftApp::new();
app.set_option("language", "en").unwrap();
app.open_bytes("boxes.pdf", None, bytes).expect("opens");
app
});
h.ctx.all_styles_mut(|s| s.animation_time = 0.2);
h.run_steps(4);
assert!(h.state_mut().execute("edit.edit_text"));
h.run_steps(2);
let r = h.state().views[0].page_screen_rect(0).expect("on screen");
let k = r.width() / 300.0;
let mut blocks = {
let s = h.state();
s.session.get(s.views[0].id).unwrap().text_blocks(0)
};
blocks.sort_by(|a, b| b.rect[1].partial_cmp(&a.rect[1]).unwrap_or(std::cmp::Ordering::Equal));
let on_screen = |b: &pdfcraft_engine::TextBlock| {
egui::Rect::from_min_max(
egui::pos2(r.left() + b.rect[0] as f32 * k, r.top() + (300.0 - b.rect[3] as f32) * k),
egui::pos2(r.left() + b.rect[2] as f32 * k, r.top() + (300.0 - b.rect[1] as f32) * k),
)
};
(h, [on_screen(&blocks[0]), on_screen(&blocks[1])])
}
/// The colour of the editor's frame (the accent stroke around `r`) this frame, if it is painted.
fn editor_frame_color(h: &Harness<'static, PdfCraftApp>, r: egui::Rect) -> Option<egui::Color32> {
h.output().shapes.iter().find_map(|shape| match &shape.shape {
egui::Shape::Rect(rect) if rect.stroke.width == 1.5 && rect.rect.contains(r.center()) => Some(rect.stroke.color),
_ => None,
})
}
/// A paragraph opens at once: it used to fade in over a fifth of a second, its frame dimmed and
/// the page's own copy of the text showing through the editor's cover.
#[test]
fn a_paragraph_editor_opens_at_full_opacity() {
let (mut h, [top, _]) = two_boxes_animated();
h.hover_at(top.center());
h.run_steps(1);
h.drag_at(top.center());
h.run_steps(1);
h.drop_at(top.center());
let accent = egui::Color32::from_rgb(0x14, 0x73, 0xE6);
for frame in 0..8 {
h.step();
// The click's own frame opens it: the editor's frame is there from then on, at full colour.
assert_eq!(editor_frame_color(&h, top), Some(accent), "frame {frame} after the click");
}
}
#[test]
fn editing_existing_text_in_place() {
let mut h = harness(1, |_| {});