mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
Merge PR #864
This commit is contained in:
commit
23aeddb354
19 files changed
+264
-26
No files matched your search
@@ -134,6 +134,7 @@ Newest first.
|
||||
- **2026-10-13** · Object › Fitting has InDesign's shortcuts: Fill Frame Proportionally ⌘⌥⇧C, Fit Content Proportionally ⌘⌥⇧E, Fit Frame to Content ⌘⌥C, Fit Content to Frame ⌘⌥E, Center Content ⌘⇧E. No readiness percentage change.
|
||||
- **2026-10-13** · Gradients between process colours blend as inks, as InDesign shows them: magenta to cyan passes through C 50 M 50 (a light lilac) on screen instead of the darker RGB mix, and a midpoint between CMYK or grey stops no longer turns the PDF shading RGB. No readiness percentage change.
|
||||
- **2026-10-14** · The Swatches panel has InDesign's header: a Fill/Stroke proxy, Formatting Affects Container/Text and the Tint of the colour in front (a tint alone keeps each object's own swatch). Clicking a swatch applies it to the fill or stroke in front (of the text, when formatting affects text), and the bottom row has New Color Group, New Swatch and Delete Swatch. No readiness percentage change.
|
||||
- **2026-10-14** · With the Page tool, the Control panel shows the page as InDesign's does: W/H from a reference point, page-size preset (US Business Card added), orientation, Liquid Page Rule and Show Parent Page Overlay, which draws the parent page's size over a resized page. No readiness percentage change.
|
||||
- **2026-10-14** · The Page tool works as InDesign's: a click selects a page and shows its handles and liquid page rule; dragging a handle previews the rule and springs back, Alt-drag resizes the page from the opposite edge, Shift keeps proportions, with a W/H readout. No readiness percentage change.
|
||||
- **2026-10-12** · Text threads by hand: click a selected text frame's in or out port to load
|
||||
the text cursor, then click a frame to thread it, click empty space for a column-wide frame or
|
||||
|
||||
@@ -226,6 +226,7 @@ impl Document {
|
||||
liquid: Default::default(),
|
||||
transition: None,
|
||||
view_rotation: 0,
|
||||
show_parent_overlay: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,9 @@ pub struct Page {
|
||||
/// Liquid page rule: how objects follow when the page changes size.
|
||||
#[serde(default, skip_serializing_if = "LiquidRule::is_off")]
|
||||
pub liquid: LiquidRule,
|
||||
/// Page tool › Show Parent Page Overlay: the parent page's size drawn over this page.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
pub show_parent_overlay: bool,
|
||||
/// Window › Interactive › Page Transitions (the spread's first page holds the spread's).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub transition: Option<PageTransition>,
|
||||
@@ -208,6 +211,8 @@ pub enum LiquidRule {
|
||||
GuideBased,
|
||||
/// Each object's pins and resize settings decide (`Item::liquid`).
|
||||
ObjectBased,
|
||||
/// The rule of the page's parent page (InDesign's "Controlled by Parent"; Off without one).
|
||||
ControlledByParent,
|
||||
}
|
||||
|
||||
impl LiquidRule {
|
||||
@@ -368,6 +373,7 @@ mod tests {
|
||||
liquid: Default::default(),
|
||||
transition: None,
|
||||
view_rotation: 0,
|
||||
show_parent_overlay: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -380,6 +380,15 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_doc,
|
||||
page_size
|
||||
),
|
||||
cmd!(
|
||||
"layout.pageOptions",
|
||||
"Page Options",
|
||||
[],
|
||||
None,
|
||||
"{pages: [1-based], showParentOverlay?: bool} — Page tool options (Show Parent Page Overlay: the parent page's size drawn over the page)",
|
||||
has_doc,
|
||||
page_options
|
||||
),
|
||||
cmd!(noundo "layout.selectPage", "Select Page", [], None, "{page: 1-based | null} — the page the Page tool selects (its handles, the Control panel)", has_doc, |s, p| {
|
||||
let n = s.doc()?.doc.page_count();
|
||||
let page = match p.get("page") {
|
||||
@@ -664,6 +673,26 @@ fn page_size(s: &mut crate::Session, p: &Value) -> Result<Value> {
|
||||
})
|
||||
}
|
||||
|
||||
fn page_options(s: &mut crate::Session, p: &Value) -> Result<Value> {
|
||||
let pages: Vec<usize> =
|
||||
p.get("pages").and_then(Value::as_array).map(|a| a.iter().filter_map(Value::as_u64).map(|n| n as usize).collect()).unwrap_or_default();
|
||||
let n = s.doc()?.doc.page_count();
|
||||
if pages.is_empty() || pages.iter().any(|x| *x == 0 || *x > n) {
|
||||
return Err(bad("layout.pageOptions", "give `pages` (1-based)"));
|
||||
}
|
||||
let overlay = p.get("showParentOverlay").and_then(Value::as_bool);
|
||||
s.edit(|d, _| {
|
||||
for page in &pages {
|
||||
let Some((si, pi)) = d.page_loc(page - 1) else { continue };
|
||||
let pg = &mut std::sync::Arc::make_mut(&mut d.spreads[si]).pages[pi];
|
||||
if let Some(v) = overlay {
|
||||
pg.show_parent_overlay = v;
|
||||
}
|
||||
}
|
||||
Ok(json!({"pages": pages.len()}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Give `pages` (1-based) their own size. Objects stay on their page; pages with a liquid rule
|
||||
/// re-flow their objects by it.
|
||||
pub(crate) fn resize_pages(d: &mut designcraft_doc::Document, pages: &[usize], w: Option<f64>, h: Option<f64>) {
|
||||
|
||||
@@ -35,7 +35,7 @@ fn axis(lo: f64, hi: f64, o: (f64, f64), n: (f64, f64), pin_lo: bool, pin_hi: bo
|
||||
fn target(rule: LiquidRule, b: Rect, old: Rect, new: Rect, liquid: Option<ObjectLiquid>, vguides: &[f64], hguides: &[f64]) -> Rect {
|
||||
let (ox, oy, nx, ny) = ((old.x0, old.x1), (old.y0, old.y1), (new.x0, new.x1), (new.y0, new.y1));
|
||||
match rule {
|
||||
LiquidRule::Off => b,
|
||||
LiquidRule::Off | LiquidRule::ControlledByParent => b,
|
||||
LiquidRule::ReCenter => b + (new.center() - old.center()),
|
||||
LiquidRule::Scale => {
|
||||
let k = (new.width() / old.width()).min(new.height() / old.height());
|
||||
@@ -63,7 +63,7 @@ fn target(rule: LiquidRule, b: Rect, old: Rect, new: Rect, liquid: Option<Object
|
||||
pub(crate) fn apply(d: &mut Document, sr: SpreadRef, pi: usize, old: Rect) {
|
||||
let Some(sp) = d.spread(sr) else { return };
|
||||
let Some(pg) = sp.pages.get(pi) else { return };
|
||||
let (rule, new) = (pg.liquid, pg.bounds());
|
||||
let (rule, new) = (effective_rule(d, sr, pi), pg.bounds());
|
||||
if rule == LiquidRule::Off || old == new || old.width() < 1e-6 || old.height() < 1e-6 {
|
||||
return;
|
||||
}
|
||||
@@ -108,8 +108,24 @@ pub(crate) fn apply(d: &mut Document, sr: SpreadRef, pi: usize, old: Rect) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The rule page `pi` of spread `sr` follows: its own, or its parent page's when it's controlled
|
||||
/// by the parent (Off when that's unset too).
|
||||
pub(crate) fn effective_rule(d: &Document, sr: SpreadRef, pi: usize) -> LiquidRule {
|
||||
let Some(pg) = d.spread(sr).and_then(|sp| sp.pages.get(pi)) else { return LiquidRule::Off };
|
||||
if pg.liquid != LiquidRule::ControlledByParent {
|
||||
return pg.liquid;
|
||||
}
|
||||
let SpreadRef::Doc(si) = sr else { return LiquidRule::Off };
|
||||
let abs = d.first_page_of_spread(si) + pi;
|
||||
match d.parent_page_for(abs).and_then(|(ppi, ppg)| d.parents.get(ppi)?.pages.get(ppg).map(|p| p.liquid)) {
|
||||
Some(LiquidRule::ControlledByParent) | None => LiquidRule::Off,
|
||||
Some(r) => r,
|
||||
}
|
||||
}
|
||||
|
||||
fn rule_param(p: &Value, id: &str) -> Result<LiquidRule> {
|
||||
serde_json::from_value(p.get("rule").cloned().unwrap_or(json!("off"))).map_err(|_| bad(id, "`rule`: off|scale|reCenter|guideBased|objectBased"))
|
||||
serde_json::from_value(p.get("rule").cloned().unwrap_or(json!("off")))
|
||||
.map_err(|_| bad(id, "`rule`: off|scale|reCenter|guideBased|objectBased|controlledByParent"))
|
||||
}
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
@@ -119,7 +135,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Liquid Page Rule",
|
||||
["Layout", "Liquid Layout"],
|
||||
None,
|
||||
"{rule: off|scale|reCenter|guideBased|objectBased, pages?: [1-based] (default: all)}",
|
||||
"{rule: off|scale|reCenter|guideBased|objectBased|controlledByParent (the parent page's rule), pages?: [1-based] (default: all)}",
|
||||
has_doc,
|
||||
|s, p| {
|
||||
let rule = rule_param(p, "liquid.pageRule")?;
|
||||
|
||||
@@ -643,6 +643,31 @@ fn page_size_keeps_objects_by_its_anchor() {
|
||||
assert_eq!(s.execute("layout.selectPage", &json!({"page": 1})).unwrap()["page"], 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn controlled_by_parent_takes_the_parent_pages_rule() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 400, "height": 400, "facingPages": false})).unwrap();
|
||||
let id = s.execute("frame.create", &json!({"rect": [100, 100, 200, 200]})).unwrap()["id"].as_u64().unwrap();
|
||||
let frame = |s: &Session| s.doc().unwrap().doc.item(designcraft_doc::ItemId(id)).unwrap().bounds();
|
||||
// The page follows its parent; the parent page re-centres.
|
||||
s.execute("liquid.pageRule", &json!({"rule": "controlledByParent", "pages": [1]})).unwrap();
|
||||
s.edit(|d, _| {
|
||||
std::sync::Arc::make_mut(&mut d.parents[0]).pages[0].liquid = designcraft_doc::LiquidRule::ReCenter;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
s.execute("layout.pageSize", &json!({"pages": [1], "width": 600})).unwrap();
|
||||
assert_eq!(frame(&s).x0, 200.0, "re-centred: {:?}", frame(&s));
|
||||
// Without a parent (or a parent that's also controlled by its parent) nothing moves.
|
||||
s.execute("layout.pages.applyParent", &json!({"pages": [0], "parent": null})).unwrap();
|
||||
s.execute("layout.pageSize", &json!({"pages": [1], "width": 800})).unwrap();
|
||||
assert_eq!(frame(&s).x0, 200.0);
|
||||
// Show Parent Page Overlay is a page option.
|
||||
s.execute("layout.pageOptions", &json!({"pages": [1], "showParentOverlay": true})).unwrap();
|
||||
assert!(s.doc().unwrap().doc.spreads[0].pages[0].show_parent_overlay);
|
||||
assert!(s.execute("layout.pageOptions", &json!({"pages": [2], "showParentOverlay": true})).is_err());
|
||||
}
|
||||
|
||||
/// Load the text from a frame's out port with the Selection tool and click on the pasteboard:
|
||||
/// a new frame is threaded there and the overset text flows into it.
|
||||
#[test]
|
||||
|
||||
@@ -1977,6 +1977,7 @@ impl<'r> Importer<'r> {
|
||||
liquid: Default::default(),
|
||||
transition: None,
|
||||
view_rotation: 0,
|
||||
show_parent_overlay: false,
|
||||
});
|
||||
}
|
||||
let overrides: Vec<Vec<String>> =
|
||||
|
||||
@@ -198,6 +198,7 @@ pub fn rule_label(rule: designcraft_doc::LiquidRule) -> &'static str {
|
||||
R::ReCenter => "Re-center",
|
||||
R::ObjectBased => "Object-Based",
|
||||
R::GuideBased => "Guide-Based",
|
||||
R::ControlledByParent => "Controlled by Parent",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -664,8 +664,18 @@ fn draw_guides(app: &DesignApp, painter: &egui::Painter, xf: &Xf, doc: &Document
|
||||
painter.rect_stroke(rect(br), 0.0, Stroke::new(hair(painter), c32(s.bleed_color)), StrokeKind::Middle);
|
||||
}
|
||||
let pasteboard = m.inverse().transform_rect_bbox(layout.pasteboard);
|
||||
for p in &sp.pages {
|
||||
for (pi, p) in sp.pages.iter().enumerate() {
|
||||
let pr = p.bounds();
|
||||
// Page tool › Show Parent Page Overlay: the parent page's size over the page.
|
||||
if p.show_parent_overlay
|
||||
&& let SpreadRef::Doc(si) = slot.spread
|
||||
&& let Some((ppi, ppg)) = doc.parent_page_for(doc.first_page_of_spread(si) + pi)
|
||||
&& let Some(pp) = doc.parents.get(ppi).and_then(|ps| ps.pages.get(ppg))
|
||||
{
|
||||
let r = rect(DRect::new(pr.x0, pr.y0, pr.x0 + pp.width, pr.y0 + pp.height));
|
||||
painter.rect_filled(r, 0.0, Color32::from_rgba_unmultiplied(110, 110, 230, 28));
|
||||
painter.rect_stroke(r, 0.0, Stroke::new(1.0, Color32::from_rgb(110, 110, 230)), StrokeKind::Middle);
|
||||
}
|
||||
if !app.ui.guides {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,9 @@ pub fn control_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
ui.horizontal_top(|ui| {
|
||||
let text_mode =
|
||||
matches!(app.session.tool_id(), "type" | "verticalType") || app.session.active().is_some_and(|d| d.selection.text.is_some());
|
||||
if text_mode {
|
||||
if app.session.tool_id() == "page" {
|
||||
control_page(app, ui);
|
||||
} else if text_mode {
|
||||
control_text(app, ui);
|
||||
} else {
|
||||
control_object(app, ui);
|
||||
@@ -192,6 +194,93 @@ pub fn control_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
});
|
||||
}
|
||||
|
||||
/// The Control panel with the Page tool, as InDesign's: reference point, X/Y, W/H, page-size
|
||||
/// preset, orientation, Liquid Page Rule and Show Parent Page Overlay, for
|
||||
/// the page the Page tool selected (else the page in view). W/H keep the reference point in place.
|
||||
fn control_page(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
let lang = app.ui.language.clone();
|
||||
let Some(st) = app.session.active() else { return };
|
||||
let units = st.doc.settings.horizontal_units;
|
||||
let Some(page) = st.selection.page.or_else(|| crate::canvas::current_page(app)) else { return };
|
||||
let Some(st) = app.session.active() else { return };
|
||||
let d = st.doc.clone();
|
||||
let Some((si, pi)) = d.page_loc(page) else { return };
|
||||
let pg = d.spreads[si].pages[pi].clone();
|
||||
let n = page + 1;
|
||||
// Reference point: W/H edits keep it in place.
|
||||
let (r, _) = ui.allocate_exact_size(vec2(30.0, 44.0), Sense::hover());
|
||||
if let Some(p) = panels::properties::ref_point_proxy(ui, egui::Rect::from_center_size(r.center(), vec2(27.0, 27.0)), app.ui.ref_point) {
|
||||
app.ui.ref_point = p;
|
||||
}
|
||||
let anchor = app.ui.ref_point.min(8);
|
||||
let (fx, fy) = ((anchor % 3) as f64 / 2.0, (anchor / 3) as f64 / 2.0);
|
||||
// The reference point's position: from the spine across, from the top down.
|
||||
let (x, y) = (pg.x - d.spreads[si].spine_x() + fx * pg.width, fy * pg.height);
|
||||
egui::Grid::new("ctl_page_xywh").num_columns(4).spacing(vec2(4.0, 4.0)).show(ui, |ui| {
|
||||
caption(ui, crate::i18n::tr(&lang, "X:"));
|
||||
// Pages don't move within their spread yet.
|
||||
ui.add_enabled_ui(false, |ui| measure(ui, "cpx", Some(x), units, 64.0));
|
||||
caption(ui, crate::i18n::tr(&lang, "W:"));
|
||||
if let Some(v) = measure(ui, "cpw", Some(pg.width), units, 64.0) {
|
||||
let _ = app.run("layout.pageSize", json!({"pages": [n], "width": v, "anchor": anchor}));
|
||||
}
|
||||
ui.end_row();
|
||||
caption(ui, crate::i18n::tr(&lang, "Y:"));
|
||||
ui.add_enabled_ui(false, |ui| measure(ui, "cpy", Some(y), units, 64.0));
|
||||
caption(ui, crate::i18n::tr(&lang, "H:"));
|
||||
if let Some(v) = measure(ui, "cph", Some(pg.height), units, 64.0) {
|
||||
let _ = app.run("layout.pageSize", json!({"pages": [n], "height": v, "anchor": anchor}));
|
||||
}
|
||||
ui.end_row();
|
||||
});
|
||||
vsep(ui);
|
||||
// Page-size preset and orientation.
|
||||
let landscape = pg.width > pg.height;
|
||||
ui.vertical(|ui| {
|
||||
let cur = panels::properties::preset_name(pg.width, pg.height);
|
||||
egui::ComboBox::from_id_salt("ctl_page_preset").selected_text(crate::rtl::widget(ui, crate::i18n::tr(&lang, cur))).width(130.0).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
for (name, w, h) in panels::properties::PAGE_PRESETS {
|
||||
if ui.selectable_label(cur == *name, crate::rtl::widget(ui, crate::i18n::tr(&lang, name))).clicked() {
|
||||
let (w, h) = if landscape { (h, w) } else { (w, h) };
|
||||
let _ = app.run("layout.pageSize", json!({"pages": [n], "width": w, "height": h, "anchor": anchor}));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
ui.horizontal(|ui| {
|
||||
for (icon, tip, want) in [("orient-portrait", "Portrait", false), ("orient-landscape", "Landscape", true)] {
|
||||
if crate::widgets::icon_toggle(ui, icon, landscape == want, crate::i18n::tr(&lang, tip)).clicked() && landscape != want {
|
||||
let _ = app.run("layout.pageSize", json!({"pages": [n], "width": pg.height, "height": pg.width, "anchor": anchor}));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
vsep(ui);
|
||||
ui.vertical(|ui| {
|
||||
caption(ui, crate::i18n::tr(&lang, "Liquid Page Rule:"));
|
||||
let label = panels::interactive::liquid_rule_label(pg.liquid);
|
||||
egui::ComboBox::from_id_salt("ctl_page_rule").selected_text(crate::rtl::widget(ui, crate::i18n::tr(&lang, label))).width(150.0).show_ui(
|
||||
ui,
|
||||
|ui| {
|
||||
for (r, l) in panels::interactive::LIQUID_RULES {
|
||||
if ui.selectable_label(pg.liquid == r, crate::rtl::widget(ui, crate::i18n::tr(&lang, l))).clicked() && pg.liquid != r {
|
||||
let _ = app.run("liquid.pageRule", json!({"rule": r, "pages": [n]}));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
vsep(ui);
|
||||
ui.vertical(|ui| {
|
||||
let mut overlay = pg.show_parent_overlay;
|
||||
if ui.checkbox(&mut overlay, crate::rtl::widget(ui, crate::i18n::tr(&lang, "Show Parent Page Overlay"))).changed() {
|
||||
let _ = app.run("layout.pageOptions", json!({"pages": [n], "showParentOverlay": overlay}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// A field label drawn as a tool icon (hover for its name).
|
||||
fn icon_caption(ui: &mut egui::Ui, icon: &str, tip: &str) {
|
||||
let (r, resp) = ui.allocate_exact_size(vec2(16.0, 16.0), Sense::hover());
|
||||
@@ -830,6 +919,25 @@ mod tests {
|
||||
assert!(short.min.x - first.min.x <= 2.0 * super::DOC_TAB_MAX_W, "the last tab {short:?} starts too far right");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_page_tool_has_its_own_control_panel() {
|
||||
// Chris Cicchino's Page Tool and Liquid Layout video (1:02): with the Page tool, the
|
||||
// Control panel shows the page's size, preset, orientation and liquid page rule.
|
||||
let mut app = app();
|
||||
app.run("file.new", serde_json::json!({"width": 252, "height": 144, "facingPages": false})).unwrap();
|
||||
app.session.set_tool("page");
|
||||
let mut h = test_window::open(app, vec2(1800.0, 600.0));
|
||||
let _ = h.get_by_label("Liquid Page Rule:");
|
||||
// Pages don't move within their spread, so there is no "Objects Move with Page" option, and
|
||||
// no "Controlled by Parent" rule until parent pages have rules of their own.
|
||||
assert!(h.query_by_label("Objects Move with Page").is_none());
|
||||
assert!(!crate::panels::interactive::LIQUID_RULES.iter().any(|(r, _)| *r == designcraft_doc::LiquidRule::ControlledByParent));
|
||||
assert_eq!(crate::panels::properties::preset_name(252.0, 144.0), "US Business Card");
|
||||
h.get_by_label("Show Parent Page Overlay").click();
|
||||
h.run_steps(4);
|
||||
assert!(h.state().app.session.doc().unwrap().doc.spreads[0].pages[0].show_parent_overlay);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_narrow_window_scrolls_the_control_bar() {
|
||||
let w = 700.0;
|
||||
|
||||
@@ -2519,6 +2519,25 @@ const TABLE: &[(&str, [&str; 5])] = &[
|
||||
("Object-Based", ["Object-Based", "Object-Based", "Object-Based", "Object-Based", "基于对象"]),
|
||||
("Guide-Based", ["Guide-Based", "Guide-Based", "Guide-Based", "Guide-Based", "基于参考线"]),
|
||||
("Resize Page", ["Seitengröße ändern", "Redimensionner la page", "Cambiar tamaño de página", "ページのサイズを変更", "调整页面大小"]),
|
||||
(
|
||||
"Liquid Page Rule:",
|
||||
["Regel für flüssige Seiten:", "Règle de page liquide :", "Regla de página líquida:", "リキッドページルール:", "自适应页面规则:"],
|
||||
),
|
||||
(
|
||||
"Show Parent Page Overlay",
|
||||
[
|
||||
"Überlagerung der übergeordneten Seite anzeigen",
|
||||
"Afficher la superposition de la page parent",
|
||||
"Mostrar superposición de página principal",
|
||||
"親ページオーバーレイを表示",
|
||||
"显示父页面叠加",
|
||||
],
|
||||
),
|
||||
(
|
||||
"Controlled by Parent",
|
||||
["Durch übergeordnete Seite gesteuert", "Contrôlé par le parent", "Controlado por principal", "親ページで制御", "由父页面控制"],
|
||||
),
|
||||
("US Business Card", ["US-Visitenkarte", "Carte de visite US", "Tarjeta de visita EE. UU.", "US 名刺", "美国名片"]),
|
||||
("Loop", ["Loop", "Loop", "Loop", "Loop", "循环播放"]),
|
||||
("Object-based", ["Object-based", "Object-based", "Object-based", "Object-based", "基于对象"]),
|
||||
("Off", ["Off", "Off", "Off", "Off", "关闭"]),
|
||||
|
||||
@@ -721,6 +721,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "استنادًا إلى الكائنات"),
|
||||
("Guide-Based", "استنادًا إلى الأدلة"),
|
||||
("Resize Page", "تغيير حجم الصفحة"),
|
||||
("Liquid Page Rule:", "قاعدة الصفحة المرنة:"),
|
||||
("Show Parent Page Overlay", "إظهار تراكب الصفحة الأصل"),
|
||||
("Controlled by Parent", "تتحكم فيه الصفحة الأصل"),
|
||||
("US Business Card", "بطاقة عمل أمريكية"),
|
||||
("Loop", "تكرار"),
|
||||
("Object-based", "استنادًا إلى الكائنات"),
|
||||
("Play on Page Load", "تشغيل عند تحميل الصفحة"),
|
||||
|
||||
@@ -1299,6 +1299,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "Basato su oggetti"),
|
||||
("Guide-Based", "Basato su guide"),
|
||||
("Resize Page", "Ridimensiona pagina"),
|
||||
("Liquid Page Rule:", "Regola pagina liquida:"),
|
||||
("Show Parent Page Overlay", "Mostra sovrapposizione pagina principale"),
|
||||
("Controlled by Parent", "Controllato dalla pagina principale"),
|
||||
("US Business Card", "Biglietto da visita USA"),
|
||||
("Loop", "Ciclo continuo"),
|
||||
("Object-based", "Basato su oggetti"),
|
||||
("Off", "Disattivato"),
|
||||
|
||||
@@ -1295,6 +1295,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "オブジェクト基準"),
|
||||
("Guide-Based", "ガイド基準"),
|
||||
("Resize Page", "ページのサイズを変更"),
|
||||
("Liquid Page Rule:", "リキッドページルール:"),
|
||||
("Show Parent Page Overlay", "親ページオーバーレイを表示"),
|
||||
("Controlled by Parent", "親ページで制御"),
|
||||
("US Business Card", "US 名刺"),
|
||||
("Loop", "繰り返し再生"),
|
||||
("Object-based", "オブジェクト基準"),
|
||||
("Off", "オフ"),
|
||||
|
||||
@@ -712,6 +712,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "Baseado em objetos"),
|
||||
("Guide-Based", "Baseado em guias"),
|
||||
("Resize Page", "Redimensionar página"),
|
||||
("Liquid Page Rule:", "Regra de página líquida:"),
|
||||
("Show Parent Page Overlay", "Mostrar sobreposição da página principal"),
|
||||
("Controlled by Parent", "Controlado pela página principal"),
|
||||
("US Business Card", "Cartão de visita EUA"),
|
||||
("Loop", "Repetir"),
|
||||
("Object-based", "Baseado em objetos"),
|
||||
("Play on Page Load", "Reproduzir ao carregar a página"),
|
||||
|
||||
@@ -1296,6 +1296,10 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "На основі об’єктів"),
|
||||
("Guide-Based", "На основі напрямних"),
|
||||
("Resize Page", "Змінити розмір сторінки"),
|
||||
("Liquid Page Rule:", "Правило гнучкої сторінки:"),
|
||||
("Show Parent Page Overlay", "Показати накладення батьківської сторінки"),
|
||||
("Controlled by Parent", "Керується батьківською сторінкою"),
|
||||
("US Business Card", "Візитна картка (США)"),
|
||||
("Loop", "Зациклити"),
|
||||
("Object-based", "На основі об’єктів"),
|
||||
("Off", "Вимкнено"),
|
||||
|
||||
@@ -486,6 +486,20 @@ pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Liquid page rules in InDesign's order, with their menu labels.
|
||||
pub const LIQUID_RULES: [(designcraft_doc::LiquidRule, &str); 5] = [
|
||||
(designcraft_doc::LiquidRule::Off, "Off"),
|
||||
(designcraft_doc::LiquidRule::Scale, "Scale"),
|
||||
(designcraft_doc::LiquidRule::ReCenter, "Re-center"),
|
||||
(designcraft_doc::LiquidRule::ObjectBased, "Object-based"),
|
||||
(designcraft_doc::LiquidRule::GuideBased, "Guide-based"),
|
||||
];
|
||||
|
||||
/// A liquid page rule's menu label.
|
||||
pub fn liquid_rule_label(rule: designcraft_doc::LiquidRule) -> &'static str {
|
||||
LIQUID_RULES.iter().find(|(r, _)| *r == rule).map_or("Off", |(_, l)| l)
|
||||
}
|
||||
|
||||
/// Liquid Layout panel: the current page's rule, the selection's pins (object-based), the page's
|
||||
/// liquid guides, and Create Alternate Layout.
|
||||
pub fn liquid(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
@@ -501,23 +515,9 @@ pub fn liquid(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
crate::rtl::label(ui, egui::RichText::new(format!("{} {}", crate::i18n::tr(&app.ui.language, "Page"), d.page_name(page))).strong());
|
||||
let mut rule = pg.liquid;
|
||||
egui::ComboBox::from_id_salt("liquid_rule")
|
||||
.selected_text(crate::rtl::widget(
|
||||
ui,
|
||||
crate::i18n::tr(
|
||||
&app.ui.language,
|
||||
match rule {
|
||||
L::Off => "Off",
|
||||
L::Scale => "Scale",
|
||||
L::ReCenter => "Re-center",
|
||||
L::GuideBased => "Guide-based",
|
||||
L::ObjectBased => "Object-based",
|
||||
},
|
||||
),
|
||||
))
|
||||
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, liquid_rule_label(rule))))
|
||||
.show_ui(ui, |ui| {
|
||||
for (r, label) in
|
||||
[(L::Off, "Off"), (L::Scale, "Scale"), (L::ReCenter, "Re-center"), (L::GuideBased, "Guide-based"), (L::ObjectBased, "Object-based")]
|
||||
{
|
||||
for (r, label) in LIQUID_RULES {
|
||||
ui.selectable_value(&mut rule, r, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label)));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
|
||||
// ---------------------------------------------------------------- No Selection
|
||||
|
||||
/// Page-size presets for the Document dropdown (portrait, points).
|
||||
const PAGE_PRESETS: &[(&str, f64, f64)] = &[
|
||||
pub(crate) const PAGE_PRESETS: &[(&str, f64, f64)] = &[
|
||||
("Letter", 612.0, 792.0),
|
||||
("Legal", 612.0, 1008.0),
|
||||
("Tabloid", 792.0, 1224.0),
|
||||
@@ -171,9 +171,10 @@ const PAGE_PRESETS: &[(&str, f64, f64)] = &[
|
||||
("A5", 419.528, 595.276),
|
||||
("A6", 297.638, 419.528),
|
||||
("B5", 498.898, 708.661),
|
||||
("US Business Card", 144.0, 252.0),
|
||||
];
|
||||
|
||||
fn preset_name(w: f64, h: f64) -> &'static str {
|
||||
pub(crate) fn preset_name(w: f64, h: f64) -> &'static str {
|
||||
let (a, b) = (w.min(h), w.max(h));
|
||||
PAGE_PRESETS.iter().find(|p| (p.1 - a).abs() < 0.6 && (p.2 - b).abs() < 0.6).map(|p| p.0).unwrap_or("Custom")
|
||||
}
|
||||
@@ -386,7 +387,7 @@ fn document_sections(app: &mut DesignApp, ui: &mut Ui) {
|
||||
// ---------------------------------------------------------------- Objects
|
||||
|
||||
/// The 9-point reference-point proxy; returns a newly picked point (0..8, row-major).
|
||||
fn ref_point_proxy(ui: &mut Ui, r: Rect, cur: u8) -> Option<u8> {
|
||||
pub(crate) fn ref_point_proxy(ui: &mut Ui, r: Rect, cur: u8) -> Option<u8> {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let mut out = None;
|
||||
let pitch = r.width() / 3.0;
|
||||
|
||||
@@ -398,7 +398,7 @@ It is evidence of breadth: each item exists. It is not a claim of InDesign-level
|
||||
- Type on a Path (⇧T tool, `type.onPath`): a story along any line, curve or shape on screen and in PDF; Type on a Path Options (start, flip, baseline/ascender/descender/centre) and Delete Type from Path. Round-trips through IDML as TextPath; on-path caret drawing is still to come.
|
||||
- Preferences › Appearance of Black: 100% K on screen accurately (dark grey) or as rich black (`window.richBlack`); the printing/export side is still to come.
|
||||
- Spreads of up to 10 pages: Add to Previous Spread and Allow Spread to Shuffle (Pages panel; `layout.pages.toSpread`, `layout.spreadShuffle`); inserted pages go around kept spreads.
|
||||
- Page tool (⇧P) and `layout.pageSize`: pages of their own size (or a preset); the spine stays put and objects keep their place on their page. A click selects a page (handles, its liquid page rule tagged above it); dragging a handle previews the liquid rule and springs back, Alt-drag resizes, Shift keeps proportions, with a W/H readout; `anchor` keeps a reference point in place; double-click for exact sizes.
|
||||
- Page tool (⇧P) and `layout.pageSize`: pages of their own size (or a preset); the spine stays put and objects keep their place on their page. A click selects a page (handles, its liquid page rule tagged above it); dragging a handle previews the liquid rule and springs back, Alt-drag resizes, Shift keeps proportions, with a W/H readout; `anchor` keeps a reference point in place; double-click for exact sizes. With the Page tool the Control panel shows the page (reference point, W/H, preset, orientation, Liquid Page Rule, Show Parent Page Overlay); X/Y are shown, but pages don't move within their spread yet.
|
||||
- File › Print (⌘P): printer, copies, page range, spreads, marks, bleed; printed as PDF through the system spooler (`file.print`, `file.printers`, `dryRun` for agents). Shortcuts now act like choosing the menu item (dialogs open), and UI commands win shortcut ties (⌘N opens New Document).
|
||||
- Nested styles (through/up to N sentences, words, characters, letters, digits, tabs, breaks, spaces or given characters) and GREP styles (regex → character style), edited in Paragraph Style Options and composed under local formatting; `ui.dialog.open` for agents. Nested line styles are still to come; nested and GREP styles round-trip through IDML.
|
||||
- Table and cell styles (round-trip through IDML): cell styles (fill, insets, vertical justification, strokes, paragraph style) and table styles (region cell styles, border, alternating rows, spacing); apply from the Table panel, edits re-apply to every user; `style.cell.*`, `style.table.*`.
|
||||
|
||||
Reference in new issue
Block a user