mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 14:22:39 +00:00
The Text Frame Options dialog filled its fields from the document's ruler unit but never set the dialog's own unit, so it stayed at picas. The spinner fields (Top, Bottom, Left, Right, a non-rectangular frame's Inset, the column width and gutter, Paragraph Spacing Limit) showed their values in picas whatever Preferences > Units & Increments said, and a bare number typed in any measure field, Min under First Baseline included, was read as picas. The dialog now takes the document's horizontal ruler unit when it opens and keeps it while open, as the Rectangle dialog does (#1964).
This commit is contained in:
1 parent
618c191166
commit
40cc78d8f8
3 files changed
+61
-1
No files matched your search
@@ -394,6 +394,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
## Progress log
|
||||
|
||||
- **2026-11-14** · Object › Text Frame Options shows the inset spacing (one Inset for a non-rectangular frame), the column width and gutter and the paragraph spacing limit in the document's ruler units, as the Properties panel does, instead of always in picas, and a bare number typed in any of its measure fields is in those units (#2739). No readiness percentage change.
|
||||
- **2026-11-12** · Properties › Text Frame shows the frame's inset spacing (Top, Bottom, Left and Right with a link that makes all four the same; one Inset for a non-rectangular frame) and four Vertical Justification buttons (Top, Center, Bottom, Justify), and the section also shows with the Type tool in a frame, where it changes the frame that shows the insertion point. No readiness percentage change.
|
||||
- **2026-11-12** · An edit of a long threaded story composes the paragraphs it can change and copies the other lines from the composition before the edit; every typed character composed the whole story. In a 240-paragraph story a typed character composes 3 paragraphs (240 before). An edit that moves lines into other frames composes on until a paragraph starts in the same frame and place as before, or to the story's end, and stories with footnotes, tables, anchored objects, balanced columns or keep options that move lines are composed whole. No readiness percentage change.
|
||||
- **2026-11-12** · Composing a story with endnotes, anchored objects or cross-references counts their marks in one pass over the story, instead of counting again from the story start for every paragraph. No readiness percentage change.
|
||||
|
||||
@@ -1165,7 +1165,10 @@ fn seed_text_frame_options(app: &DesignApp, d: &mut Dialog) {
|
||||
return;
|
||||
}
|
||||
let o = selected_frame_options(app).unwrap_or_default();
|
||||
let units = app.session.active().map(|s| s.doc.settings.horizontal_units).unwrap_or(Unit::Picas);
|
||||
let units = doc_unit(app);
|
||||
// The spinner fields show their measures in it and a bare number typed is in it (#2739):
|
||||
// kept from when the dialog opened, as the Rectangle dialog keeps its units.
|
||||
d.unit = units;
|
||||
let mut shown = text_frame_fields(&o, units, selected_frame_width(app), &document_grid(app));
|
||||
// A non-rectangular frame shows one Inset (its top inset, the one kept from the outline).
|
||||
let shaped = selected_frame_is_shaped(app);
|
||||
|
||||
@@ -1796,6 +1796,62 @@ fn text_frame_options_dims_the_sides_for_a_non_rectangular_frame() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Text Frame Options shows the insets (or a non-rectangular frame's one Inset), the gutter and
|
||||
/// the other measures in the document's ruler unit, not always in picas, and reads a bare number
|
||||
/// typed there in that unit (#2739).
|
||||
#[test]
|
||||
fn text_frame_options_follow_the_ruler_units() {
|
||||
use egui::accesskit::Role;
|
||||
use egui_kittest::{Harness, kittest::Queryable};
|
||||
let mm = |pt: f64| format_measure(pt, Unit::Millimeters);
|
||||
for shape in ["rectangle", "ellipse"] {
|
||||
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({})).unwrap();
|
||||
app.run("document.preferences", json!({"horizontalUnits": "millimeters", "verticalUnits": "millimeters"})).unwrap();
|
||||
let r = app.run("frame.create", json!({"rect": [36, 36, 336, 236], "shape": shape, "content": "text", "text": "Hello"})).unwrap();
|
||||
let id = designcraft_doc::ItemId(r["id"].as_u64().unwrap());
|
||||
app.run("object.textFrameOptions", json!({"ids": [id.0], "columns": 2, "gutter": 18, "inset": [9, 9, 9, 9]})).unwrap();
|
||||
app.run("selection.set", json!({"ids": [id.0]})).unwrap();
|
||||
app.ui.dialog = Some(Dialog::new("textFrameOptions", json!({})));
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1000.0, 800.0)).build_ui_state(
|
||||
|ui, app: &mut DesignApp| {
|
||||
let key = egui::Id::unique("text_frame_units_test_fonts");
|
||||
if ui.data(|d| d.get_temp::<bool>(key)).is_none() {
|
||||
crate::theme::install_fonts(ui.ctx(), "");
|
||||
ui.data_mut(|d| d.insert_temp(key, true));
|
||||
return;
|
||||
}
|
||||
show(app, ui.ctx());
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(3);
|
||||
let value = |h: &Harness<'_, DesignApp>, name: &str| h.get_by_role_and_label(Role::TextInput, name).value().unwrap_or_default();
|
||||
let sides: &[&str] = if shape == "ellipse" { &["Inset"] } else { &["Top", "Left", "Bottom", "Right"] };
|
||||
for side in sides {
|
||||
assert_eq!(value(&h, side), mm(9.0), "{shape}: {side}");
|
||||
}
|
||||
assert_eq!(value(&h, "Gutter"), mm(18.0), "{shape}: the gutter");
|
||||
assert_eq!(value(&h, "Paragraph Spacing Limit"), mm(0.0), "{shape}: the paragraph spacing limit");
|
||||
let width = value(&h, "Width");
|
||||
assert!(width.ends_with(" mm"), "{shape}: the column width {width:?} in millimetres");
|
||||
// Bare numbers typed in the fields are millimetres.
|
||||
let mut app = h.into_state();
|
||||
let d = app.ui.dialog.as_mut().expect("the dialog stays open");
|
||||
assert_eq!(d.unit, Unit::Millimeters, "{shape}");
|
||||
let inset_key = if shape == "ellipse" { "inset" } else { "insetTop" };
|
||||
for (k, v) in [(inset_key, "5"), ("gutter", "4"), ("firstBaselineMin", "2")] {
|
||||
d.fields.insert(k.into(), json!(v));
|
||||
}
|
||||
confirm(&mut app).unwrap();
|
||||
let o = app.session.doc().unwrap().doc.item(id).unwrap().text_frame().unwrap().options.clone();
|
||||
let pt = |n: f64| Unit::Millimeters.to_pt(n);
|
||||
assert!((o.inset[0] - pt(5.0)).abs() < 1e-6, "{shape}: the inset {:?}", o.inset);
|
||||
assert!((o.gutter - pt(4.0)).abs() < 1e-6, "{shape}: the gutter {}", o.gutter);
|
||||
assert!((o.first_baseline_min - pt(2.0)).abs() < 1e-6, "{shape}: the first baseline minimum {}", o.first_baseline_min);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_frame_options_change_only_what_was_edited() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
||||
Reference in new issue
Block a user