mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-14 19:27:07 -04:00
feat(ui): Control panel for the Page tool; Controlled by Parent; Show Parent Page Overlay
Chris Cicchino's "Adobe InDesign – Page Tool and Liquid Layout" (1:02–
2:06, 13:47) sizes pages from the Control panel with the Page tool
selected: X/Y, W/H, the page-size preset ("US Business Card"),
orientation, Liquid Page Rule ("Controlled by Parent"), Objects Move with
Page and Show Parent Page Overlay. DesignCraft's Control panel showed the
(empty) object controls.
- ui: with the Page tool, the Control panel shows the selected page (or
the page in view): the reference point (W/H, preset and orientation
keep it in place via layout.pageSize's anchor), X/Y (read-only),
W/H, the preset dropdown, portrait/landscape, Liquid Page Rule, Objects
Move with Page (always on: pages don't move within spreads yet) and
Show Parent Page Overlay.
- doc: LiquidRule::ControlledByParent (the parent page's rule, Off
without one; resolved by liquid::effective_rule); Page.show_parent_overlay.
- engine: layout.pageOptions {pages, showParentOverlay}; liquid.pageRule
takes controlledByParent.
- canvas: the parent page's size over a page with the overlay on.
- Liquid Layout panel and Control panel share InDesign's rule list
(Off, Scale, Re-center, Object-based, Guide-based, Controlled by
Parent); "US Business Card" preset; translations.
Builds on the Page tool PR (#862) for the selected page.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
cc13e80c9d
commit
59b6125af1
18 files changed
+280
-25
No files matched your search
@@ -129,6 +129,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
Newest first.
|
||||
|
||||
- **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 (now with Controlled by Parent) 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** · macOS Dock Quit uses the same unsaved-document prompts as window and menu Quit. Repeated requests keep the current answers; Cancel retains every tab and unsaved recovery checkpoint, and completed Save/Don't Save answers exit through the ordinary event loop. No readiness percentage change.
|
||||
- **2026-10-12** · Orderly native exit writes the latest recovery state after finalizing current quit approvals; incomplete or stale plans keep their documents, and Undo to the saved state clears stale recovery. Unavailable or failed recovery is reported. Cancelable macOS Dock Quit remains open (#544). No readiness percentage change.
|
||||
|
||||
@@ -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 {
|
||||
@@ -367,6 +372,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")?;
|
||||
|
||||
@@ -642,3 +642,28 @@ fn page_size_keeps_objects_by_its_anchor() {
|
||||
assert!(s.execute("layout.selectPage", &json!({"page": 2})).is_err());
|
||||
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());
|
||||
}
|
||||
@@ -1963,6 +1963,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",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -604,8 +604,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,96 @@ 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, Objects Move with Page 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| {
|
||||
// Objects follow a page that moves in its spread; pages don't move yet, so it's always on.
|
||||
let mut follow = true;
|
||||
ui.add_enabled(false, egui::Checkbox::new(&mut follow, crate::rtl::widget(ui, crate::i18n::tr(&lang, "Objects Move with Page"))));
|
||||
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 +922,23 @@ 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));
|
||||
for label in ["Liquid Page Rule:", "Objects Move with Page"] {
|
||||
let _ = h.get_by_label(label);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -2501,6 +2501,35 @@ 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:", "リキッドページルール:", "自适应页面规则:"],
|
||||
),
|
||||
(
|
||||
"Objects Move with Page",
|
||||
[
|
||||
"Objekte mit Seite verschieben",
|
||||
"Déplacer les objets avec la page",
|
||||
"Los objetos se mueven con la página",
|
||||
"オブジェクトをページと共に移動",
|
||||
"对象随页面移动",
|
||||
],
|
||||
),
|
||||
(
|
||||
"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", "关闭"]),
|
||||
|
||||
@@ -712,6 +712,11 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "استنادًا إلى الكائنات"),
|
||||
("Guide-Based", "استنادًا إلى الأدلة"),
|
||||
("Resize Page", "تغيير حجم الصفحة"),
|
||||
("Liquid Page Rule:", "قاعدة الصفحة المرنة:"),
|
||||
("Objects Move with Page", "تحريك الكائنات مع الصفحة"),
|
||||
("Show Parent Page Overlay", "إظهار تراكب الصفحة الأصل"),
|
||||
("Controlled by Parent", "تتحكم فيه الصفحة الأصل"),
|
||||
("US Business Card", "بطاقة عمل أمريكية"),
|
||||
("Loop", "تكرار"),
|
||||
("Object-based", "استنادًا إلى الكائنات"),
|
||||
("Play on Page Load", "تشغيل عند تحميل الصفحة"),
|
||||
|
||||
@@ -1298,6 +1298,11 @@ 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:"),
|
||||
("Objects Move with Page", "Sposta oggetti con la pagina"),
|
||||
("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"),
|
||||
|
||||
@@ -1294,6 +1294,11 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "オブジェクト基準"),
|
||||
("Guide-Based", "ガイド基準"),
|
||||
("Resize Page", "ページのサイズを変更"),
|
||||
("Liquid Page Rule:", "リキッドページルール:"),
|
||||
("Objects Move with Page", "オブジェクトをページと共に移動"),
|
||||
("Show Parent Page Overlay", "親ページオーバーレイを表示"),
|
||||
("Controlled by Parent", "親ページで制御"),
|
||||
("US Business Card", "US 名刺"),
|
||||
("Loop", "繰り返し再生"),
|
||||
("Object-based", "オブジェクト基準"),
|
||||
("Off", "オフ"),
|
||||
|
||||
@@ -711,6 +711,11 @@ 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:"),
|
||||
("Objects Move with Page", "Objetos se movem com a página"),
|
||||
("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"),
|
||||
|
||||
@@ -1295,6 +1295,11 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Object-Based", "На основі об’єктів"),
|
||||
("Guide-Based", "На основі напрямних"),
|
||||
("Resize Page", "Змінити розмір сторінки"),
|
||||
("Liquid Page Rule:", "Правило гнучкої сторінки:"),
|
||||
("Objects Move with Page", "Об’єкти рухаються зі сторінкою"),
|
||||
("Show Parent Page Overlay", "Показати накладення батьківської сторінки"),
|
||||
("Controlled by Parent", "Керується батьківською сторінкою"),
|
||||
("US Business Card", "Візитна картка (США)"),
|
||||
("Loop", "Зациклити"),
|
||||
("Object-based", "На основі об’єктів"),
|
||||
("Off", "Вимкнено"),
|
||||
|
||||
@@ -486,6 +486,21 @@ 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); 6] = [
|
||||
(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"),
|
||||
(designcraft_doc::LiquidRule::ControlledByParent, "Controlled by Parent"),
|
||||
];
|
||||
|
||||
/// 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 +516,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;
|
||||
|
||||
Reference in new issue
Block a user