mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-26 19:26:46 -06:00
feat(ui): Text Frame Options laid out as InDesign's, with its General options
InDesign's Text Frame Options has a section list (General, Baseline Options, Column Rules, ...). General holds Columns (Fixed Number / Fixed Width with Number, Width, Gutter), Balance Columns, Inset Spacing as a 2x2 grid with a link button, Vertical Justification and Ignore Text Wrap; Baseline Options holds First Baseline Offset and Min. DesignCraft showed one flat list without the columns type, width, balance, link, ignore-wrap or baseline settings, although the model keeps them. - dialogs: a section list (General, Baseline Options, Column Rules); General as above (Width shows what Fixed Number columns get, editable for Fixed Width; a side typed while linked sets all four insets); Baseline Options; Column Rules as before. Enum captions translated. - OK sends the new fields only when edited, as before. - engine: `object.textFrameOptions` takes `columnWidth` (Fixed Width) and `firstBaselineMin`, which compose already honours. LYH Studio episode 14 sets two columns (9:45) and a 0.1875 in inset (10:57) here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e862e0a613
commit
61ea089149
@@ -129,6 +129,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-10-13** · Text Frame Options is laid out as InDesign's: General (Columns: Fixed Number or Fixed Width with Number, Width and Gutter; Balance Columns; Inset Spacing with the link button; Vertical Justification; Ignore Text Wrap), Baseline Options (First Baseline Offset and Min) and Column Rules. `object.textFrameOptions` takes `columnWidth` and `firstBaselineMin`. 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.
|
||||
- **2026-10-12** · Recovered documents keep a checkpoint until saved, closed or reverted, including a second crash before autosave. Restarted document IDs and explicit recovery folders preserve other snapshots; failed republishing retains the original. No readiness percentage change.
|
||||
|
||||
@@ -532,7 +532,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Text Frame Options…",
|
||||
["Object"],
|
||||
Some("Cmd+B"),
|
||||
"{columns?, gutter?, inset?: number|[t,l,b,r] (null keeps that side), verticalJustification?: top|center|bottom|justify, firstBaseline?, autoSize?, ignoreWrap?, balanceColumns?, columnRule?: bool, columnRuleWeight? (pt), columnRuleColor? (swatch name), columnRuleTint? (0..1), columnRuleOffset? (pt, horizontal), columnRuleTopInset? (pt), columnRuleBottomInset? (pt), vertical?: bool (sets the story direction of the frames' stories, as Type ▸ Story Direction), ids?}",
|
||||
"{columns?, gutter?, inset?: number|[t,l,b,r] (null keeps that side), verticalJustification?: top|center|bottom|justify, columnsKind?: fixedNumber|fixedWidth|flexibleWidth, columnWidth? (pt, Fixed Width), firstBaseline?: ascent|capHeight|leading|xHeight|fixed, firstBaselineMin? (pt), autoSize?, ignoreWrap?, balanceColumns?, columnRule?: bool, columnRuleWeight? (pt), columnRuleColor? (swatch name), columnRuleTint? (0..1), columnRuleOffset? (pt, horizontal), columnRuleTopInset? (pt), columnRuleBottomInset? (pt), vertical?: bool (sets the story direction of the frames' stories, as Type ▸ Story Direction), ids?}",
|
||||
has_selection,
|
||||
text_frame_options
|
||||
),
|
||||
@@ -1654,6 +1654,12 @@ fn text_frame_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if let Some(v) = p.get("ignoreWrap").and_then(Value::as_bool) {
|
||||
o.ignore_wrap = v;
|
||||
}
|
||||
if let Some(v) = p.get("columnWidth").and_then(Value::as_f64) {
|
||||
o.column_width = v.max(0.0);
|
||||
}
|
||||
if let Some(v) = p.get("firstBaselineMin").and_then(Value::as_f64) {
|
||||
o.first_baseline_min = v.max(0.0);
|
||||
}
|
||||
if let Some(v) = p.get("balanceColumns").and_then(Value::as_bool) {
|
||||
o.balance_columns = v;
|
||||
}
|
||||
|
||||
+173
-22
@@ -410,6 +410,13 @@ fn pdf_export(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
}
|
||||
|
||||
/// The options of the selected text frame (the first selected item, or the frame of the caret).
|
||||
/// The selected text frame's width (its own, before rotation).
|
||||
fn selected_frame_width(app: &DesignApp) -> Option<f64> {
|
||||
let st = app.session.active()?;
|
||||
let fid = st.selection.items.first().copied().or_else(|| st.selection.text.and_then(|t| t.frame))?;
|
||||
Some(st.doc.item(fid)?.inner_bounds().width())
|
||||
}
|
||||
|
||||
fn selected_frame_options(app: &DesignApp) -> Option<designcraft_doc::TextFrameOptions> {
|
||||
let st = app.session.active()?;
|
||||
let fid = st.selection.items.first().copied().or_else(|| st.selection.text.and_then(|t| t.frame))?;
|
||||
@@ -426,8 +433,19 @@ fn seed_text_frame_options(app: &DesignApp, d: &mut Dialog) {
|
||||
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 fm = |v: f64| json!(format_measure(v, units));
|
||||
// Fixed Number: the width each column gets in the frame (shown, as in InDesign).
|
||||
let inner = selected_frame_width(app).map(|w| w - o.inset[1] - o.inset[3]).unwrap_or(0.0);
|
||||
let n = o.columns.max(1) as f64;
|
||||
let width = match o.columns_kind {
|
||||
designcraft_doc::ColumnsKind::FixedWidth if o.column_width > 0.0 => o.column_width,
|
||||
_ => ((inner - o.gutter * (n - 1.0)) / n).max(0.0),
|
||||
};
|
||||
let kind = |v: Value| v.as_str().unwrap_or_default().to_string();
|
||||
let shown = json!({
|
||||
"columns": o.columns, "gutter": fm(o.gutter),
|
||||
"columnsKind": kind(serde_json::to_value(o.columns_kind).unwrap_or_default()), "columnWidth": fm(width),
|
||||
"balanceColumns": o.balance_columns, "ignoreWrap": o.ignore_wrap,
|
||||
"firstBaseline": kind(serde_json::to_value(o.first_baseline).unwrap_or_default()), "firstBaselineMin": fm(o.first_baseline_min),
|
||||
"insetTop": fm(o.inset[0]), "insetLeft": fm(o.inset[1]), "insetBottom": fm(o.inset[2]), "insetRight": fm(o.inset[3]),
|
||||
"verticalJustification": serde_json::to_value(o.vertical_justification).unwrap_or(Value::Null),
|
||||
"columnRule": o.column_rule, "columnRuleWeight": format_measure(o.column_rule_weight, Unit::Points), "columnRuleColor": o.column_rule_color,
|
||||
@@ -439,6 +457,7 @@ fn seed_text_frame_options(app: &DesignApp, d: &mut Dialog) {
|
||||
d.fields.entry(k.clone()).or_insert_with(|| v.clone());
|
||||
}
|
||||
}
|
||||
d.fields.entry("insetLinked".to_string()).or_insert_with(|| json!(o.inset.iter().all(|v| (v - o.inset[0]).abs() < 1e-9)));
|
||||
d.fields.insert("_shown".into(), shown);
|
||||
}
|
||||
|
||||
@@ -451,38 +470,113 @@ fn percent(t: f32) -> String {
|
||||
fn text_frame_options_dialog(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
let lang = app.ui.language.clone();
|
||||
let tr = |s: &'static str| crate::i18n::tr(&lang, s);
|
||||
egui::Grid::new("tfo").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
crate::rtl::label(ui, tr("Number of columns"));
|
||||
text_field(ui, d, "columns", 80.0);
|
||||
if d.s("section").is_empty() {
|
||||
d.fields.insert("section".into(), json!("general"));
|
||||
}
|
||||
ui.set_min_width(560.0);
|
||||
ui.horizontal_top(|ui| {
|
||||
// A fixed height: the separator would otherwise stretch the dialog to the window.
|
||||
ui.set_min_height(330.0);
|
||||
ui.set_max_height(330.0);
|
||||
section_list(&lang, ui, d, &[("general", "General"), ("baseline", "Baseline Options"), ("rules", "Column Rules")]);
|
||||
ui.separator();
|
||||
ui.vertical(|ui| match d.s("section").as_str() {
|
||||
"baseline" => {
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("First Baseline")).font(semibold(12.0)));
|
||||
egui::Grid::new("tfo_fb").num_columns(4).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
crate::rtl::label(ui, tr("Offset:"));
|
||||
translated_combo(
|
||||
&lang,
|
||||
ui,
|
||||
d,
|
||||
"firstBaseline",
|
||||
&[("ascent", "Ascent"), ("capHeight", "Cap Height"), ("leading", "Leading"), ("xHeight", "x Height"), ("fixed", "Fixed")],
|
||||
);
|
||||
crate::rtl::label(ui, tr("Min:"));
|
||||
text_field(ui, d, "firstBaselineMin", 70.0);
|
||||
ui.end_row();
|
||||
});
|
||||
}
|
||||
"rules" => column_rules(app, ui, d),
|
||||
_ => general(&lang, ui, d),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Text Frame Options › General: columns, inset spacing, vertical justification, text wrap.
|
||||
fn general(lang: &str, ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
let tr = |s: &'static str| crate::i18n::tr(lang, s);
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("Columns")).font(semibold(12.0)));
|
||||
egui::Grid::new("tfo_cols").num_columns(4).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
crate::rtl::label(ui, tr("Columns:"));
|
||||
translated_combo(lang, ui, d, "columnsKind", &[("fixedNumber", "Fixed Number"), ("fixedWidth", "Fixed Width")]);
|
||||
ui.end_row();
|
||||
crate::rtl::label(ui, tr("Gutter"));
|
||||
text_field(ui, d, "gutter", 80.0);
|
||||
crate::rtl::label(ui, tr("Number:"));
|
||||
text_field(ui, d, "columns", 70.0);
|
||||
// Fixed Number: the width follows from the frame; Fixed Width: it's set here.
|
||||
crate::rtl::label(ui, tr("Width:"));
|
||||
ui.add_enabled_ui(d.s("columnsKind") == "fixedWidth", |ui| text_field(ui, d, "columnWidth", 70.0));
|
||||
ui.end_row();
|
||||
crate::rtl::label(ui, tr("Gutter:"));
|
||||
text_field(ui, d, "gutter", 70.0);
|
||||
ui.end_row();
|
||||
});
|
||||
check(ui, d, "balanceColumns", tr("Balance Columns"));
|
||||
ui.add_space(6.0);
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("Inset spacing")).font(semibold(12.0)));
|
||||
egui::Grid::new("tfo_inset").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
for (label, key) in [("Top", "insetTop"), ("Bottom", "insetBottom"), ("Left", "insetLeft"), ("Right", "insetRight")] {
|
||||
crate::rtl::label(ui, tr(label));
|
||||
text_field(ui, d, key, 80.0);
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
egui::Grid::new("tfo_vj").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
crate::rtl::label(ui, tr("Vertical justification"));
|
||||
let cur = d.s("verticalJustification");
|
||||
egui::ComboBox::from_id_salt("vj").selected_text(crate::rtl::widget(ui, crate::i18n::tr(&lang, &cur))).show_ui(ui, |ui| {
|
||||
for v in ["top", "center", "bottom", "justify"] {
|
||||
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&lang, v))).clicked() {
|
||||
d.fields.insert("verticalJustification".into(), json!(v));
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("Inset Spacing")).font(semibold(12.0)));
|
||||
// While linked, a side typed in sets all four ("Make all settings the same").
|
||||
let sides = ["insetTop", "insetLeft", "insetBottom", "insetRight"];
|
||||
let before: Vec<String> = sides.iter().map(|k| d.s(k)).collect();
|
||||
// Two rows, Top | Left and Bottom | Right, the link between the columns (fixed-width
|
||||
// captions; the second column starts where the first row's does).
|
||||
let mut col2_x: Option<f32> = None;
|
||||
for (r, row) in [[("Top:", "insetTop"), ("Left:", "insetLeft")], [("Bottom:", "insetBottom"), ("Right:", "insetRight")]].into_iter().enumerate() {
|
||||
ui.horizontal(|ui| {
|
||||
for (k, (label, key)) in row.into_iter().enumerate() {
|
||||
if k == 1 {
|
||||
match col2_x {
|
||||
Some(x) => ui.add_space((x - ui.cursor().min.x).max(0.0)),
|
||||
None => col2_x = Some(ui.cursor().min.x),
|
||||
}
|
||||
}
|
||||
let x0 = ui.cursor().min.x;
|
||||
crate::rtl::label(ui, tr(label));
|
||||
ui.add_space(((if k == 0 { 56.0 } else { 44.0 }) - (ui.cursor().min.x - x0)).max(0.0));
|
||||
text_field(ui, d, key, 70.0);
|
||||
if k == 0 && r == 0 {
|
||||
let linked = d.b("insetLinked");
|
||||
let tip = tr("Make all settings the same");
|
||||
if crate::widgets::icon_toggle_sized(ui, if linked { "link" } else { "link-broken" }, linked, tip, egui::vec2(22.0, 21.0))
|
||||
.clicked()
|
||||
{
|
||||
d.fields.insert("insetLinked".into(), json!(!linked));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if d.b("insetLinked")
|
||||
&& let Some(k) = sides.iter().zip(&before).find(|(k, b)| d.s(k) != **b).map(|(k, _)| *k)
|
||||
{
|
||||
let v = d.fields.get(k).cloned().unwrap_or_default();
|
||||
for side in sides {
|
||||
d.fields.insert(side.into(), v.clone());
|
||||
}
|
||||
}
|
||||
ui.add_space(6.0);
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("Vertical Justification")).font(semibold(12.0)));
|
||||
egui::Grid::new("tfo_vj").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
|
||||
crate::rtl::label(ui, tr("Align:"));
|
||||
translated_combo(lang, ui, d, "verticalJustification", &[("top", "Top"), ("center", "Center"), ("bottom", "Bottom"), ("justify", "Justify")]);
|
||||
ui.end_row();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
crate::rtl::label(ui, egui::RichText::new(tr("Column Rules")).font(semibold(12.0)));
|
||||
check(ui, d, "ignoreWrap", tr("Ignore Text Wrap"));
|
||||
}
|
||||
|
||||
/// Text Frame Options › Column Rules.
|
||||
fn column_rules(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
|
||||
let tr = |s: &'static str| crate::i18n::tr(&app.ui.language, s);
|
||||
check(ui, d, "columnRule", tr("Insert Column Rule"));
|
||||
let swatches: Vec<String> = app
|
||||
.session
|
||||
@@ -554,6 +648,23 @@ fn confirm_text_frame_options(app: &mut DesignApp, d: &mut Dialog) -> Result<Val
|
||||
if edited(d, "verticalJustification") {
|
||||
p.insert("verticalJustification".into(), json!(d.s("verticalJustification")));
|
||||
}
|
||||
for key in ["columnsKind", "firstBaseline"] {
|
||||
if edited(d, key) {
|
||||
p.insert(key.into(), json!(d.s(key)));
|
||||
}
|
||||
}
|
||||
// The width counts only for Fixed Width columns (Fixed Number shows it).
|
||||
if d.s("columnsKind") == "fixedWidth" && (edited(d, "columnWidth") || edited(d, "columnsKind")) {
|
||||
p.insert("columnWidth".into(), json!(d.m("columnWidth").ok_or_else(|| bad("the column width is not a measure"))?));
|
||||
}
|
||||
if edited(d, "firstBaselineMin") {
|
||||
p.insert("firstBaselineMin".into(), json!(d.m("firstBaselineMin").ok_or_else(|| bad("the first baseline minimum is not a measure"))?));
|
||||
}
|
||||
for key in ["balanceColumns", "ignoreWrap"] {
|
||||
if edited(d, key) {
|
||||
p.insert(key.into(), json!(d.b(key)));
|
||||
}
|
||||
}
|
||||
if edited(d, "columnRule") {
|
||||
p.insert("columnRule".into(), json!(d.b("columnRule")));
|
||||
}
|
||||
@@ -4350,6 +4461,46 @@ mod tests {
|
||||
assert!(dialog.fields["status"].as_str().unwrap().contains("page size out of range"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_frame_options_set_columns_baseline_and_wrap_as_indesign_does() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
app.run("file.new", json!({})).unwrap();
|
||||
// A 300 pt frame, 2 columns, 12 pt gutter (picas in the dialog).
|
||||
let r = app.run("frame.create", json!({"rect": [36, 36, 336, 236], "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": 12})).unwrap();
|
||||
let opts = |app: &DesignApp| app.session.doc().unwrap().doc.item(id).unwrap().text_frame().unwrap().options.clone();
|
||||
let mut d = Dialog::new("textFrameOptions", json!({}));
|
||||
seed_text_frame_options(&app, &mut d);
|
||||
// Fixed Number shows each column's width: (300 - 12) / 2 = 144 pt.
|
||||
assert_eq!((d.s("columnsKind"), d.s("columnWidth"), d.s("firstBaseline")), ("fixedNumber".into(), "12p0".into(), "ascent".into()));
|
||||
for (k, v) in [
|
||||
("columnsKind", json!("fixedWidth")),
|
||||
("columnWidth", json!("10p0")),
|
||||
("firstBaseline", json!("capHeight")),
|
||||
("firstBaselineMin", json!("0p6")),
|
||||
("balanceColumns", json!(true)),
|
||||
("ignoreWrap", json!(true)),
|
||||
] {
|
||||
d.fields.insert(k.into(), v);
|
||||
}
|
||||
app.ui.dialog = Some(d);
|
||||
confirm(&mut app).unwrap();
|
||||
let o = opts(&app);
|
||||
assert_eq!(o.columns_kind, designcraft_doc::ColumnsKind::FixedWidth);
|
||||
assert_eq!((o.column_width, o.first_baseline_min, o.columns), (120.0, 6.0, 2));
|
||||
assert_eq!(o.first_baseline, designcraft_doc::FirstBaseline::CapHeight);
|
||||
assert!(o.balance_columns && o.ignore_wrap);
|
||||
// Fixed Number again: the width isn't sent.
|
||||
let mut d = Dialog::new("textFrameOptions", json!({}));
|
||||
seed_text_frame_options(&app, &mut d);
|
||||
assert_eq!(d.s("columnWidth"), "10p0", "Fixed Width shows its own width");
|
||||
d.fields.insert("columnsKind".into(), json!("fixedNumber"));
|
||||
app.ui.dialog = Some(d);
|
||||
confirm(&mut app).unwrap();
|
||||
assert_eq!((opts(&app).columns_kind, opts(&app).column_width), (designcraft_doc::ColumnsKind::FixedNumber, 120.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_frame_options_change_only_what_was_edited() {
|
||||
let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
|
||||
|
||||
@@ -55,6 +55,20 @@ const TABLE: &[(&str, [&str; 5])] = &[
|
||||
("Cut", ["Ausschneiden", "Couper", "Cortar", "カット", "剪切"]),
|
||||
("Copy", ["Kopieren", "Copier", "Copiar", "コピー", "复制"]),
|
||||
("Paste", ["Einfügen", "Coller", "Pegar", "ペースト", "粘贴"]),
|
||||
("Baseline Options", ["Grundlinienoptionen", "Options de ligne de base", "Opciones de línea de base", "ベースラインオプション", "基线选项"]),
|
||||
("Fixed Number", ["Feste Anzahl", "Nombre fixe", "Número fijo", "固定数", "固定数字"]),
|
||||
("Fixed Width", ["Feste Breite", "Largeur fixe", "Anchura fija", "固定幅", "固定宽度"]),
|
||||
("Number:", ["Anzahl:", "Nombre :", "Número:", "数:", "数字:"]),
|
||||
("Gutter:", ["Spaltenabstand:", "Gouttière :", "Medianil:", "間隔:", "栏间距:"]),
|
||||
("Balance Columns", ["Spalten ausgleichen", "Équilibrer les colonnes", "Equilibrar columnas", "段を均等化", "平衡栏"]),
|
||||
("Inset Spacing", ["Versatzabstand", "Marge intérieure", "Espaciado de margen interior", "フレーム内マージン", "内边距"]),
|
||||
("Top:", ["Oben:", "Haut :", "Superior:", "上:", "上:"]),
|
||||
("Left:", ["Links:", "Gauche :", "Izquierda:", "左:", "左:"]),
|
||||
("Bottom:", ["Unten:", "Bas :", "Inferior:", "下:", "下:"]),
|
||||
("Right:", ["Rechts:", "Droite :", "Derecha:", "右:", "右:"]),
|
||||
("Vertical Justification", ["Vertikale Ausrichtung", "Justification verticale", "Justificación vertical", "垂直比率", "垂直对齐"]),
|
||||
("Align:", ["Ausrichten:", "Alignement :", "Alinear:", "整列:", "对齐:"]),
|
||||
("Ignore Text Wrap", ["Textumfluss ignorieren", "Ignorer l’habillage", "Ignorar ceñir texto", "テキストの回り込みを無視", "忽略文本绕排"]),
|
||||
("Reverse Path", ["Pfadrichtung umkehren", "Inverser le tracé", "Invertir trazado", "パスの方向反転", "反转路径"]),
|
||||
("Set Anchor", ["Ankerpunkt festlegen", "Définir le point d’ancrage", "Definir punto de ancla", "アンカーポイントを設定", "设置锚点"]),
|
||||
("Clear", ["Löschen", "Effacer", "Borrar", "消去", "清除"]),
|
||||
|
||||
@@ -30,6 +30,20 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "قص"),
|
||||
("Copy", "نسخ"),
|
||||
("Paste", "لصق"),
|
||||
("Baseline Options", "خيارات خط الأساس"),
|
||||
("Fixed Number", "عدد ثابت"),
|
||||
("Fixed Width", "عرض ثابت"),
|
||||
("Number:", "العدد:"),
|
||||
("Gutter:", "التباعد:"),
|
||||
("Balance Columns", "موازنة الأعمدة"),
|
||||
("Inset Spacing", "تباعد الإزاحة الداخلية"),
|
||||
("Top:", "أعلى:"),
|
||||
("Left:", "يسار:"),
|
||||
("Bottom:", "أسفل:"),
|
||||
("Right:", "يمين:"),
|
||||
("Vertical Justification", "الضبط العمودي"),
|
||||
("Align:", "المحاذاة:"),
|
||||
("Ignore Text Wrap", "تجاهل التفاف النص"),
|
||||
("Reverse Path", "عكس المسار"),
|
||||
("Set Anchor", "تعيين نقطة الارتساء"),
|
||||
("Clear", "مسح"),
|
||||
|
||||
@@ -28,6 +28,20 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Taglia"),
|
||||
("Copy", "Copia"),
|
||||
("Paste", "Incolla"),
|
||||
("Baseline Options", "Opzioni linea di base"),
|
||||
("Fixed Number", "Numero fisso"),
|
||||
("Fixed Width", "Larghezza fissa"),
|
||||
("Number:", "Numero:"),
|
||||
("Gutter:", "Spaziatura:"),
|
||||
("Balance Columns", "Bilancia colonne"),
|
||||
("Inset Spacing", "Spaziatura interna"),
|
||||
("Top:", "Superiore:"),
|
||||
("Left:", "Sinistra:"),
|
||||
("Bottom:", "Inferiore:"),
|
||||
("Right:", "Destra:"),
|
||||
("Vertical Justification", "Giustificazione verticale"),
|
||||
("Align:", "Allinea:"),
|
||||
("Ignore Text Wrap", "Ignora contorna con testo"),
|
||||
("Reverse Path", "Inverti tracciato"),
|
||||
("Set Anchor", "Imposta ancoraggio"),
|
||||
("Clear", "Cancella"),
|
||||
|
||||
@@ -30,6 +30,18 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "カット"),
|
||||
("Copy", "コピー"),
|
||||
("Paste", "ペースト"),
|
||||
("Baseline Options", "ベースラインオプション"),
|
||||
("Column Rules", "段落境界線"),
|
||||
("Insert Column Rule", "段落境界線を挿入"),
|
||||
("Fixed Number", "固定数"),
|
||||
("Fixed Width", "固定幅"),
|
||||
("Number:", "数:"),
|
||||
("Gutter:", "間隔:"),
|
||||
("Balance Columns", "段を均等化"),
|
||||
("Inset Spacing", "フレーム内マージン"),
|
||||
("Vertical Justification", "垂直比率"),
|
||||
("Align:", "整列:"),
|
||||
("Ignore Text Wrap", "テキストの回り込みを無視"),
|
||||
("Reverse Path", "パスの方向反転"),
|
||||
("Set Anchor", "アンカーポイントを設定"),
|
||||
("Clear", "消去"),
|
||||
|
||||
@@ -31,6 +31,20 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Recortar"),
|
||||
("Copy", "Copiar"),
|
||||
("Paste", "Colar"),
|
||||
("Baseline Options", "Opções de linha de base"),
|
||||
("Fixed Number", "Número fixo"),
|
||||
("Fixed Width", "Largura fixa"),
|
||||
("Number:", "Número:"),
|
||||
("Gutter:", "Medianiz:"),
|
||||
("Balance Columns", "Equilibrar colunas"),
|
||||
("Inset Spacing", "Espaçamento interno"),
|
||||
("Top:", "Superior:"),
|
||||
("Left:", "Esquerda:"),
|
||||
("Bottom:", "Inferior:"),
|
||||
("Right:", "Direita:"),
|
||||
("Vertical Justification", "Justificação vertical"),
|
||||
("Align:", "Alinhar:"),
|
||||
("Ignore Text Wrap", "Ignorar disposição do texto"),
|
||||
("Reverse Path", "Inverter demarcador"),
|
||||
("Set Anchor", "Definir ponto de ancoragem"),
|
||||
("Clear", "Limpar"),
|
||||
|
||||
@@ -31,6 +31,22 @@ pub(super) const TABLE: &[(&str, &str)] = &[
|
||||
("Cut", "Вирізати"),
|
||||
("Copy", "Копіювати"),
|
||||
("Paste", "Вставити"),
|
||||
("Baseline Options", "Параметри базової лінії"),
|
||||
("Column Rules", "Лінії між колонками"),
|
||||
("Insert Column Rule", "Вставити лінію між колонками"),
|
||||
("Fixed Number", "Фіксована кількість"),
|
||||
("Fixed Width", "Фіксована ширина"),
|
||||
("Number:", "Кількість:"),
|
||||
("Gutter:", "Проміжок:"),
|
||||
("Balance Columns", "Вирівняти колонки"),
|
||||
("Inset Spacing", "Внутрішні відступи"),
|
||||
("Top:", "Угорі:"),
|
||||
("Left:", "Ліворуч:"),
|
||||
("Bottom:", "Унизу:"),
|
||||
("Right:", "Праворуч:"),
|
||||
("Vertical Justification", "Вертикальне вирівнювання"),
|
||||
("Align:", "Вирівняти:"),
|
||||
("Ignore Text Wrap", "Ігнорувати обтікання текстом"),
|
||||
("Reverse Path", "Розвернути контур"),
|
||||
("Set Anchor", "Задати прив’язку"),
|
||||
("Clear", "Очистити"),
|
||||
|
||||
Reference in New Issue
Block a user