UI: add translation for remained UI

This commit is contained in:
Shao Wenbin
2026-10-06 15:12:49 -04:00
committed by Brandon Thomas
parent 9d85777546
commit 11900ad923
23 changed files with 2653 additions and 580 deletions
+6 -1
View File
@@ -113,7 +113,12 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "DesignCraft")).font(semibold(26.0)).color(t.text_strong),
));
ui.label(egui::RichText::new(format!("Version {}", env!("CARGO_PKG_VERSION"))).size(12.0).color(t.text_dim));
crate::rtl::label(
ui,
egui::RichText::new(format!("{} {}", crate::i18n::tr(&app.ui.language, "Version"), env!("CARGO_PKG_VERSION")))
.size(12.0)
.color(t.text_dim),
);
ui.add_space(4.0);
ui.label(crate::rtl::widget(
ui,
+51 -31
View File
@@ -22,7 +22,7 @@ pub fn app_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
let mut menus_end = full.min.x;
let mut tools_start = full.max.x;
ui.horizontal_centered(|ui| {
if icons::button(ui, "home", 24.0, app.session.active().is_none(), "Home").clicked() {
if icons::button(ui, "home", 24.0, app.session.active().is_none(), crate::i18n::tr(&app.ui.language, "Home")).clicked() {
app.session_home();
}
ui.add_space(6.0);
@@ -40,10 +40,18 @@ pub fn app_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
let (r, _) = ui.allocate_exact_size(vec2(125.0, 18.0), Sense::click());
ui.painter().rect(r, 1.0, t.input, Stroke::new(1.0, t.field_border), egui::StrokeKind::Inside);
icons::paint(ui.painter(), egui::Rect::from_min_size(r.min + vec2(3.0, 2.0), vec2(14.0, 14.0)), "search", t.icon);
ui.painter().text(r.min + vec2(20.0, 9.0), egui::Align2::LEFT_CENTER, "Search", egui::FontId::proportional(11.0), t.text_dim);
crate::rtl::paint(
ui.painter(),
r.min + vec2(20.0, 9.0),
egui::Align2::LEFT_CENTER,
crate::i18n::tr(&app.ui.language, "Search"),
egui::FontId::proportional(11.0),
t.text_dim,
);
ui.add_space(8.0);
let current = app.ui.workspace.clone();
ui.menu_button(egui::RichText::new(format!("{current} ▾")).font(semibold(11.5)).color(t.text), |ui| {
let shown = crate::i18n::workspace_name(&app.ui.language, &current);
ui.menu_button(crate::rtl::widget(ui, egui::RichText::new(format!("{shown} ▾")).font(semibold(11.5)).color(t.text)), |ui| {
let customs: Vec<String> = app.ui.custom_workspaces.iter().map(|w| w.name.clone()).collect();
for w in
["Essentials", "Advanced", "Book", "Digital Publishing", "Interactive for PDF", "Printing and Proofing", "Typography"]
@@ -51,13 +59,20 @@ pub fn app_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
.map(str::to_string)
.chain(customs.iter().cloned())
{
if ui.selectable_label(w == current, &w).clicked() {
if ui.selectable_label(w == current, crate::rtl::widget(ui, crate::i18n::workspace_name(&app.ui.language, &w))).clicked()
{
let _ = app.run("window.workspace", json!({"name": w}));
ui.close();
}
}
ui.separator();
if ui.button(format!("Reset {current}")).clicked() {
if ui
.button(crate::rtl::widget(
ui,
format!("{} {}", crate::i18n::tr(&app.ui.language, "Reset"), crate::i18n::workspace_name(&app.ui.language, &current)),
))
.clicked()
{
let _ = app.run("window.resetWorkspace", json!({}));
ui.close();
}
@@ -77,7 +92,7 @@ pub fn app_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
}
});
ui.add_space(6.0);
if icons::button(ui, "share", 22.0, false, "Share").clicked() {
if icons::button(ui, "share", 22.0, false, crate::i18n::tr(&app.ui.language, "Share")).clicked() {
app.status("Export a PDF, IDML or package to share — no cloud account needed.");
}
ui.add_space(8.0);
@@ -143,7 +158,9 @@ pub fn control_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
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());
icons::paint(ui.painter(), r, icon, Tokens::get(ui.ctx()).icon);
resp.on_hover_text(tip);
resp.on_hover_ui(|ui| {
crate::rtl::label(ui, tip);
});
}
fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
@@ -159,20 +176,20 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
None => (None, None, None, None),
};
egui::Grid::new("ctl_xywh").num_columns(4).spacing(vec2(4.0, 4.0)).show(ui, |ui| {
caption(ui, "X:");
caption(ui, crate::i18n::tr(&app.ui.language, "X:"));
if let Some(v) = measure(ui, "cx", x, units, 64.0) {
let _ = app.run("transform.set", json!({"x": v}));
}
caption(ui, "W:");
caption(ui, crate::i18n::tr(&app.ui.language, "W:"));
if let Some(v) = measure(ui, "cw", w, units, 64.0) {
let _ = app.run("transform.set", json!({"width": v}));
}
ui.end_row();
caption(ui, "Y:");
caption(ui, crate::i18n::tr(&app.ui.language, "Y:"));
if let Some(v) = measure(ui, "cy", y, units, 64.0) {
let _ = app.run("transform.set", json!({"y": v}));
}
caption(ui, "H:");
caption(ui, crate::i18n::tr(&app.ui.language, "H:"));
if let Some(v) = measure(ui, "ch", h, units, 64.0) {
let _ = app.run("transform.set", json!({"height": v}));
}
@@ -183,20 +200,20 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
let tv = if info.is_some() { app.session.execute("transform.info", &json!({})).ok() } else { None };
let tv_get = |k: &str| tv.as_ref().and_then(|v| v[k].as_f64());
egui::Grid::new("ctl_srs").num_columns(4).spacing(vec2(4.0, 4.0)).show(ui, |ui| {
icon_caption(ui, "tool-scale", "Scale X Percentage");
icon_caption(ui, "tool-scale", crate::i18n::tr(&app.ui.language, "Scale X Percentage"));
if let Some(v) = number(ui, "csx", tv_get("scaleX"), "%", 56.0, 1) {
let _ = app.run("transform.set", json!({"scaleX": v}));
}
icon_caption(ui, "tool-rotate", "Rotation Angle");
icon_caption(ui, "tool-rotate", crate::i18n::tr(&app.ui.language, "Rotation Angle"));
if let Some(v) = number(ui, "crot", tv_get("rotation"), "°", 50.0, 1) {
let _ = app.run("transform.set", json!({"rotation": v}));
}
ui.end_row();
icon_caption(ui, "tool-scale", "Scale Y Percentage");
icon_caption(ui, "tool-scale", crate::i18n::tr(&app.ui.language, "Scale Y Percentage"));
if let Some(v) = number(ui, "csy", tv_get("scaleY"), "%", 56.0, 1) {
let _ = app.run("transform.set", json!({"scaleY": v}));
}
icon_caption(ui, "tool-shear", "Shear X Angle");
icon_caption(ui, "tool-shear", crate::i18n::tr(&app.ui.language, "Shear X Angle"));
if let Some(v) = number(ui, "cshr", tv_get("shear"), "°", 50.0, 1) {
let _ = app.run("transform.set", json!({"shear": v}));
}
@@ -206,7 +223,7 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
// Rotate / flip.
ui.vertical(|ui| {
ui.horizontal(|ui| {
if icons::button(ui, "tool-rotate", 20.0, false, "Rotate 90° Counterclockwise").clicked() {
if icons::button(ui, "tool-rotate", 20.0, false, crate::i18n::tr(&app.ui.language, "Rotate 90° Counterclockwise")).clicked() {
let _ = app.run("transform.rotate", json!({"angle": 90}));
}
if ui
@@ -244,13 +261,13 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
// Fill / stroke.
ui.vertical(|ui| {
ui.horizontal(|ui| {
caption(ui, "Fill");
caption(ui, crate::i18n::tr(&app.ui.language, "Fill"));
panels::swatch_picker(app, ui, "ctlfill", info.as_ref().map(|i| i.fill.clone()), |app, name| {
let _ = app.run("object.fill", json!({"swatch": name}));
});
});
ui.horizontal(|ui| {
caption(ui, "Stroke");
caption(ui, crate::i18n::tr(&app.ui.language, "Stroke"));
panels::swatch_picker(app, ui, "ctlstroke", info.as_ref().map(|i| i.stroke.clone()), |app, name| {
let _ = app.run("object.stroke", json!({"swatch": name}));
});
@@ -263,13 +280,15 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
// Opacity, fx.
ui.vertical(|ui| {
ui.horizontal(|ui| {
caption(ui, "Opacity");
caption(ui, crate::i18n::tr(&app.ui.language, "Opacity"));
if let Some(v) = number(ui, "cop", info.as_ref().map(|i| i.opacity * 100.0), "%", 42.0, 0) {
let _ = app.run("object.opacity", json!({"opacity": v / 100.0}));
}
});
ui.horizontal(|ui| {
if icons::button(ui, "panel-effects", 20.0, info.as_ref().is_some_and(|i| i.shadow), "Drop Shadow").clicked() {
if icons::button(ui, "panel-effects", 20.0, info.as_ref().is_some_and(|i| i.shadow), crate::i18n::tr(&app.ui.language, "Drop Shadow"))
.clicked()
{
let _ = app.run("object.dropShadow", json!({}));
}
});
@@ -325,7 +344,7 @@ fn control_object(app: &mut DesignApp, ui: &mut egui::Ui) {
{
vsep(ui);
ui.vertical(|ui| {
caption(ui, "Columns");
caption(ui, crate::i18n::tr(&app.ui.language, "Columns"));
if let Some(v) = number(ui, "ccols", Some(cols as f64), "", 36.0, 0) {
let _ = app.run("object.textFrameOptions", json!({"columns": v.max(1.0) as u64}));
}
@@ -359,7 +378,7 @@ fn control_text(app: &mut DesignApp, ui: &mut egui::Ui) {
if let Some(v) = number(ui, "csize", chars["size"].as_f64(), " pt", 54.0, 2) {
let _ = app.run("type.char", json!({"attrs": {"size": v}}));
}
caption(ui, "VA");
caption(ui, crate::i18n::tr(&app.ui.language, "VA"));
if let Some(v) = number(ui, "ctrack", chars["tracking"].as_f64(), "", 48.0, 0) {
let _ = app.run("type.char", json!({"attrs": {"tracking": v}}));
}
@@ -372,7 +391,7 @@ fn control_text(app: &mut DesignApp, ui: &mut egui::Ui) {
if let Some(v) = number(ui, "clead", lead, " pt", 54.0, 2) {
let _ = app.run("type.char", json!({"attrs": {"leading": {"kind": "points", "value": v}}}));
}
caption(ui, "IT");
caption(ui, crate::i18n::tr(&app.ui.language, "IT"));
if let Some(v) = number(ui, "chs", chars["hScale"].as_f64().map(|v| v * 100.0), "%", 48.0, 0) {
let _ = app.run("type.char", json!({"attrs": {"hScale": v / 100.0}}));
}
@@ -426,7 +445,7 @@ fn control_text(app: &mut DesignApp, ui: &mut egui::Ui) {
panels::para_style_picker(app, ui, &ps, ov > 0, 170.0);
let hy = para["hyphenate"].as_bool().unwrap_or(true);
let mut h = hy;
if ui.checkbox(&mut h, "Hyphenate").changed() {
if ui.checkbox(&mut h, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Hyphenate"))).changed() {
let _ = app.run("type.para", json!({"attrs": {"hyphenate": h}}));
}
});
@@ -503,10 +522,10 @@ pub fn status_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.add_space(8.0);
let n = app.session.active().map(|d| d.doc.page_count()).unwrap_or(1);
let cur = crate::canvas::current_page(app).unwrap_or(0);
if icons::button(ui, "first", 14.0, false, "First Spread").clicked() {
if icons::button(ui, "first", 14.0, false, crate::i18n::tr(&app.ui.language, "First Spread")).clicked() {
crate::canvas::go_to_page(app, 0);
}
if icons::button(ui, "prev", 14.0, false, "Previous Spread").clicked() {
if icons::button(ui, "prev", 14.0, false, crate::i18n::tr(&app.ui.language, "Previous Spread")).clicked() {
crate::canvas::go_to_page(app, cur.saturating_sub(1));
}
let name = app.session.page_label(cur);
@@ -525,10 +544,10 @@ pub fn status_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
}
});
});
if icons::button(ui, "next", 14.0, false, "Next Spread").clicked() {
if icons::button(ui, "next", 14.0, false, crate::i18n::tr(&app.ui.language, "Next Spread")).clicked() {
crate::canvas::go_to_page(app, (cur + 1).min(n - 1));
}
if icons::button(ui, "last", 14.0, false, "Last Spread").clicked() {
if icons::button(ui, "last", 14.0, false, crate::i18n::tr(&app.ui.language, "Last Spread")).clicked() {
crate::canvas::go_to_page(app, n - 1);
}
ui.add_space(12.0);
@@ -542,11 +561,12 @@ pub fn status_bar(app: &mut DesignApp, ui: &mut egui::Ui) {
4.0,
if errors == 0 { Color32::from_rgb(60, 200, 90) } else { Color32::from_rgb(235, 50, 50) },
);
ui.label(
crate::rtl::label(
ui,
egui::RichText::new(if errors == 0 {
"No errors ▾".to_string()
format!("{} ▾", crate::i18n::tr(&app.ui.language, "No errors"))
} else {
format!("{errors} error{} ▾", if errors == 1 { "" } else { "s" })
format!("{errors} {} ▾", crate::i18n::tr(&app.ui.language, if errors == 1 { "error" } else { "errors" }))
})
.font(small.clone()),
);
+236 -146
View File
@@ -107,7 +107,7 @@ fn text_field(ui: &mut egui::Ui, d: &mut Dialog, key: &str, w: f32) {
/// new keys (Esc cancels, Backspace clears).
/// Edit › Spelling › User Dictionary: added words and hyphenation exceptions (applied at once).
fn user_dictionary(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
ui.label(egui::RichText::new("Hyphenation Exceptions").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Hyphenation Exceptions")).font(semibold(12.0)));
ui.label(crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(
@@ -136,7 +136,7 @@ fn user_dictionary(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
let w = w.as_str().unwrap_or("").to_string();
ui.horizontal(|ui| {
ui.label(&w);
if ui.small_button("Remove").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Remove"))).clicked() {
let _ = app.run("hyphenation.removeException", json!({"word": w}));
}
});
@@ -146,7 +146,11 @@ fn user_dictionary(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
fn keyboard_shortcuts(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
let mut q = d.s("query");
ui.add(egui::TextEdit::singleline(&mut q).hint_text("Search commands").desired_width(f32::INFINITY));
ui.add(
egui::TextEdit::singleline(&mut q)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Search commands")))
.desired_width(f32::INFINITY),
);
d.fields.insert("query".into(), json!(q));
let recording = d.s("recording");
// Capture the next key press for the command being recorded.
@@ -188,7 +192,7 @@ fn keyboard_shortcuts(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
egui::ScrollArea::vertical().max_height(360.0).show(ui, |ui| {
egui::Grid::new("shortcuts").num_columns(3).striped(true).spacing([12.0, 4.0]).show(ui, |ui| {
for (id, label, menu) in &rows {
ui.label(label.trim_end_matches('…'))
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label.trim_end_matches('…')))
.on_hover_text(format!("{id}{}", if menu.is_empty() { String::new() } else { format!(" ({menu})") }));
let cur = crate::menus::shortcut_of(app, id).map(|s| crate::menus::shortcut_text(&s)).unwrap_or_else(|| "—".into());
let text = if recording == *id { "Press keys…".to_string() } else { cur };
@@ -197,7 +201,7 @@ fn keyboard_shortcuts(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
d.fields.insert("message".into(), json!(""));
}
if app.ui.shortcuts.contains_key(id) {
if ui.small_button("Default").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Default"))).clicked() {
let _ = app.run("window.setShortcut", json!({"id": id, "shortcut": null}));
}
} else {
@@ -303,7 +307,7 @@ fn document_setup(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
.width(110.0)
.show_ui(ui, |ui| {
for (v, l) in [("print", "Print"), ("web", "Web"), ("mobile", "Mobile")] {
if ui.selectable_label(cur == v, l).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("intent".into(), json!(v));
}
}
@@ -319,22 +323,22 @@ fn document_setup(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Start Page #:"));
text_field(ui, d, "startPage", 60.0);
ui.end_row();
ui.label("Binding:");
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Binding:"));
let rtl = d.s("binding") == "rightToLeft";
egui::ComboBox::from_id_salt("ds_binding").selected_text(if rtl { "Right to Left" } else { "Left to Right" }).width(110.0).show_ui(
ui,
|ui| {
egui::ComboBox::from_id_salt("ds_binding")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, if rtl { "Right to Left" } else { "Left to Right" })))
.width(110.0)
.show_ui(ui, |ui| {
for (v, l) in [("leftToRight", "Left to Right"), ("rightToLeft", "Right to Left")] {
if ui.selectable_label((v == "rightToLeft") == rtl, l).clicked() {
if ui.selectable_label((v == "rightToLeft") == rtl, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("binding".into(), json!(v));
}
}
},
);
});
ui.end_row();
});
ui.add_space(8.0);
ui.label(egui::RichText::new("Page Size").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Page Size")).font(semibold(12.0)));
let (w, h) = (d.m("width").unwrap_or(612.0), d.m("height").unwrap_or(792.0));
let preset =
designcraft_doc::build::PRESETS.iter().find(|p| (p.width - w).abs() < 0.5 && (p.height - h).abs() < 0.5).map_or("Custom", |p| p.name);
@@ -351,7 +355,9 @@ fn document_setup(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Orientation:"));
ui.horizontal(|ui| {
for (portrait, l) in [(true, "Portrait"), (false, "Landscape")] {
if ui.selectable_label((h >= w) == portrait, l).clicked() && (h >= w) != portrait {
if ui.selectable_label((h >= w) == portrait, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked()
&& (h >= w) != portrait
{
let (fw, fh) = (d.s("width"), d.s("height"));
d.fields.insert("width".into(), json!(fh));
d.fields.insert("height".into(), json!(fw));
@@ -370,11 +376,11 @@ fn document_setup(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
egui::Grid::new("ds_bleed").num_columns(5).spacing([8.0, 6.0]).show(ui, |ui| {
ui.label("");
for e in ["Top", "Bottom", "Inside", "Outside"] {
ui.label(e);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, e));
}
ui.end_row();
for (k, l) in [("bleed", "Bleed:"), ("slug", "Slug:")] {
ui.label(l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
for e in ["Top", "Bottom", "Inside", "Outside"] {
text_field(ui, d, &format!("{k}{e}"), 60.0);
}
@@ -431,7 +437,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
ui.vertical(|ui| {
ui.set_width(150.0);
for (id, label) in sections {
if ui.selectable_label(cur == *id, *label).clicked() {
if ui.selectable_label(cur == *id, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
d.fields.insert("section".into(), json!(id));
}
}
@@ -457,7 +463,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
}
"spelling" => {
ui.label(egui::RichText::new("Dynamic Spelling").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Dynamic Spelling")).font(semibold(12.0)));
check(ui, d, "dynamicSpelling", crate::i18n::tr(&app.ui.language, "Enable Dynamic Spelling (underline misspelled words)"));
}
"autocorrect" => {
@@ -542,7 +548,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Position"));
ui.end_row();
for (k, l) in [("superscript", "Superscript:"), ("subscript", "Subscript:")] {
ui.label(l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
for f in ["Size", "Position"] {
ui.horizontal(|ui| {
text_field(ui, d, &format!("adv.{k}{f}"), 50.0);
@@ -562,11 +568,17 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "On Screen:"));
let rich = d.b("richBlack");
egui::ComboBox::from_id_salt("pref_black")
.selected_text(if rich { "Display All Blacks as Rich Black" } else { "Display All Blacks Accurately" })
.selected_text(crate::rtl::widget(
ui,
crate::i18n::tr(
&app.ui.language,
if rich { "Display All Blacks as Rich Black" } else { "Display All Blacks Accurately" },
),
))
.width(240.0)
.show_ui(ui, |ui| {
for (v, l) in [(false, "Display All Blacks Accurately"), (true, "Display All Blacks as Rich Black")] {
if ui.selectable_label(v == rich, l).clicked() {
if ui.selectable_label(v == rich, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("richBlack".into(), json!(v));
}
}
@@ -576,11 +588,17 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Printing / Exporting:"));
let rich = d.b("richBlackOutput");
egui::ComboBox::from_id_salt("pref_black_out")
.selected_text(if rich { "Output All Blacks as Rich Black" } else { "Output All Blacks Accurately" })
.selected_text(crate::rtl::widget(
ui,
crate::i18n::tr(
&app.ui.language,
if rich { "Output All Blacks as Rich Black" } else { "Output All Blacks Accurately" },
),
))
.width(240.0)
.show_ui(ui, |ui| {
for (v, l) in [(false, "Output All Blacks Accurately"), (true, "Output All Blacks as Rich Black")] {
if ui.selectable_label(v == rich, l).clicked() {
if ui.selectable_label(v == rich, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("richBlackOutput".into(), json!(v));
}
}
@@ -596,7 +614,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
}
"interface" => {
ui.label(egui::RichText::new("UI Scaling").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "UI Scaling")).font(semibold(12.0)));
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "UI Size:"));
let mut v = d.n("uiScale").unwrap_or(100.0);
@@ -616,16 +634,18 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Relative To:"));
let cur = d.s("bg.relativeTo");
egui::ComboBox::from_id_salt("bg_rel").selected_text(if cur == "topMargin" { "Top Margin" } else { "Top of Page" }).show_ui(
ui,
|ui| {
egui::ComboBox::from_id_salt("bg_rel")
.selected_text(crate::rtl::widget(
ui,
crate::i18n::tr(&app.ui.language, if cur == "topMargin" { "Top Margin" } else { "Top of Page" }),
))
.show_ui(ui, |ui| {
for (v, l) in [("topOfPage", "Top of Page"), ("topMargin", "Top Margin")] {
if ui.selectable_label(cur == v, l).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("bg.relativeTo".into(), json!(v));
}
}
},
);
});
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Increment Every:"));
text_field(ui, d, "bg.increment", 80.0);
@@ -656,10 +676,10 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
check(ui, d, "grid.inBack", crate::i18n::tr(&app.ui.language, "Grids in Back"));
}
"guides" => {
ui.label(egui::RichText::new("Color").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Color")).font(semibold(12.0)));
egui::Grid::new("pref_guides").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (k, l) in [("marginColor", "Margins:"), ("columnColor", "Columns:"), ("bleedColor", "Bleed:"), ("slugColor", "Slug:")] {
ui.label(l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
color_field(ui, d, k);
ui.end_row();
}
@@ -703,7 +723,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
})
.show_ui(ui, |ui| {
for (v, l) in [("fast", "Fast"), ("typical", "Typical"), ("high", "High Quality")] {
if ui.selectable_label(cur == v, l).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
d.fields.insert("displayQuality".into(), json!(v));
}
}
@@ -714,15 +734,21 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Ruler Units")).font(semibold(12.0)));
egui::Grid::new("pref_units").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (key, label) in [("horizontalUnits", "Horizontal:"), ("verticalUnits", "Vertical:")] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let cur: Unit = d.fields.get(key).and_then(|v| serde_json::from_value(v.clone()).ok()).unwrap_or_default();
egui::ComboBox::from_id_salt(key).selected_text(cur.label()).width(140.0).show_ui(ui, |ui| {
for u in Unit::ALL {
if ui.selectable_label(u == cur, u.label()).clicked() {
d.fields.insert(key.into(), json!(u));
egui::ComboBox::from_id_salt(key)
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, cur.label())))
.width(140.0)
.show_ui(ui, |ui| {
for u in Unit::ALL {
if ui
.selectable_label(u == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, u.label())))
.clicked()
{
d.fields.insert(key.into(), json!(u));
}
}
}
});
});
ui.end_row();
}
});
@@ -740,7 +766,7 @@ fn preferences(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "When Scaling")).font(semibold(12.0)));
check(ui, d, "scaleStrokes", crate::i18n::tr(&app.ui.language, "Include Stroke Weight"));
ui.add_space(6.0);
ui.label(egui::RichText::new("Transform").font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Transform")).font(semibold(12.0)));
check(ui, d, "dimensionsIncludeStroke", crate::i18n::tr(&app.ui.language, "Dimensions Include Stroke Weight"));
check(ui, d, "transformationsAreTotals", crate::i18n::tr(&app.ui.language, "Transformations are Totals"));
ui.add_space(6.0);
@@ -906,7 +932,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
"findChange" => {
ui.horizontal(|ui| {
for (label, grep) in [("Text", false), ("GREP", true)] {
if ui.selectable_label(!d.b("objectMode") && d.b("grep") == grep, label).clicked() {
if ui.selectable_label(!d.b("objectMode") && d.b("grep") == grep, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
d.fields.insert("grep".into(), json!(grep));
d.fields.insert("objectMode".into(), json!(false));
}
@@ -920,10 +946,10 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
let swatches: Vec<String> = app.session.active().map(|st| st.doc.swatches.iter().filter(|w| !w.hidden).map(|w| w.name.clone()).collect()).unwrap_or_default();
egui::Grid::new("fco").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (key, label, any) in [("objFill", "Find fill:", "(any)"), ("objChangeFill", "Change fill to:", "(unchanged)")] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let cur = d.s(key);
egui::ComboBox::from_id_salt(key).selected_text(if cur.is_empty() { any } else { cur.as_str() }).width(180.0).show_ui(ui, |ui| {
if ui.selectable_label(cur.is_empty(), any).clicked() {
if ui.selectable_label(cur.is_empty(), crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, any))).clicked() {
d.fields.insert(key.into(), json!(""));
}
for w in &swatches {
@@ -938,7 +964,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
let cur = d.s("objKind");
egui::ComboBox::from_id_salt("objKind").selected_text(if cur.is_empty() { "(any)" } else { cur.as_str() }).show_ui(ui, |ui| {
for v in ["", "text", "graphic", "shape", "line", "group"] {
if ui.selectable_label(cur == v, if v.is_empty() { "(any)" } else { v }).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, if v.is_empty() { "(any)" } else { v }))).clicked() {
d.fields.insert("objKind".into(), json!(v));
}
}
@@ -975,9 +1001,9 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Search:"));
let cur = d.s("scope");
egui::ComboBox::from_id_salt("fcscope").selected_text(&cur).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt("fcscope").selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &cur))).show_ui(ui, |ui| {
for v in ["document", "story", "selection"] {
if ui.selectable_label(cur == v, v).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, v))).clicked() {
d.fields.insert("scope".into(), json!(v));
}
}
@@ -1030,7 +1056,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
"pdfImport" => {
ui.label(egui::RichText::new(d.s("path")).size(11.0));
ui.horizontal(|ui| {
ui.label(format!("Page (1–{}):", d.n("pages").unwrap_or(1.0)));
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Page (1–{count}):").replace("{count}", &d.n("pages").unwrap_or(1.0).to_string()));
text_field(ui, &mut d, "page", 60.0);
});
ui.horizontal(|ui| {
@@ -1043,7 +1069,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
"media" => crate::i18n::tr(&app.ui.language, "Media"),
_ => crate::i18n::tr(&app.ui.language, "Crop"),
};
egui::ComboBox::from_id_salt("pdf_crop").selected_text(label(&cur)).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt("pdf_crop").selected_text(crate::rtl::widget(ui, label(&cur))).show_ui(ui, |ui| {
for v in ["crop", "trim", "bleed", "art", "media"] {
if ui.selectable_label(label(&cur) == label(v), label(v)).clicked() {
d.fields.insert("crop".into(), json!(v));
@@ -1069,7 +1095,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
let profiles = d.fields.get("profiles").and_then(Value::as_array).cloned().unwrap_or_default();
egui::Grid::new("color_settings").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (key, label, kind) in [("rgb", "RGB:", "rgb"), ("cmyk", "CMYK:", "cmyk")] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let cur = d.s(key);
egui::ComboBox::from_id_salt(("ws", key)).selected_text(&cur).width(260.0).show_ui(ui, |ui| {
for pr in profiles.iter().filter(|p| p["kind"] == kind) {
@@ -1084,10 +1110,10 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Intent:"));
let cur = d.s("intent");
egui::ComboBox::from_id_salt("ws_intent")
.selected_text(designcraft_color::cms::Intent::parse(&cur).map_or("", |i| i.label()))
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, designcraft_color::cms::Intent::parse(&cur).map_or("", |i| i.label()))))
.show_ui(ui, |ui| {
for i in designcraft_color::cms::Intent::ALL {
if ui.selectable_label(i.id() == cur, i.label()).clicked() {
if ui.selectable_label(i.id() == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, i.label()))).clicked() {
d.fields.insert("intent".into(), json!(i.id()));
}
}
@@ -1116,7 +1142,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
for (menu, label) in crate::menus::all_menu_items().into_iter().filter(|(m, l)| q.is_empty() || m.to_lowercase().contains(&q) || l.to_lowercase().contains(&q)) {
let key = crate::menus::menu_key(&menu, &label);
let mut visible = !app.ui.hidden_menu_items.contains(&key);
if ui.checkbox(&mut visible, format!("{menu} › {label}")).changed() {
if ui.checkbox(&mut visible, crate::rtl::widget(ui, format!("{} › {}", crate::i18n::tr(&app.ui.language, &menu), crate::i18n::tr(&app.ui.language, &label)))).changed() {
let _ = app.run("window.hideMenuItem", json!({"item": key, "hidden": !visible}));
}
}
@@ -1128,7 +1154,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
if !d.b("removeStyles") {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Style Name Conflicts")).font(semibold(12.0)));
let conflicts: Vec<String> = d.fields.get("conflicts").and_then(Value::as_array).map(|a| a.iter().filter_map(|v| v.as_str().map(str::to_string)).collect()).unwrap_or_default();
ui.label(if conflicts.is_empty() { "No conflicts".to_string() } else { format!("{} conflict(s): {}", conflicts.len(), conflicts.join(", ")) });
ui.label(if conflicts.is_empty() { crate::i18n::tr(&app.ui.language, "No conflicts").to_string() } else { format!("{} conflict(s): {}", conflicts.len(), conflicts.join(", ")) });
let cur = d.s("styleConflicts");
ui.horizontal(|ui| {
for (v, l) in [("useExisting", "Use Document Style Definition"), ("redefine", "Redefine Document Style"), ("autoRename", "Auto Rename")] {
@@ -1146,7 +1172,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
ui.label(w);
let mapped = d.fields.get("map").and_then(|m| m.get(w)).and_then(Value::as_str).unwrap_or("").to_string();
let shown = if mapped.is_empty() { "(import)".to_string() } else { mapped.clone() };
egui::ComboBox::from_id_salt(("imp_map", w)).selected_text(shown).width(170.0).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt(("imp_map", w)).selected_text(crate::rtl::widget(ui, &shown)).width(170.0).show_ui(ui, |ui| {
if ui.selectable_label(mapped.is_empty(),crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "(import)"))).clicked()
&& let Some(m) = d.fields.get_mut("map").and_then(Value::as_object_mut)
{
@@ -1187,9 +1213,9 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
};
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Fitting:"));
egui::ComboBox::from_id_salt("ff_fit").selected_text(label(&cur)).width(200.0).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt("ff_fit").selected_text(crate::rtl::widget(ui, label(&cur))).width(200.0).show_ui(ui, |ui| {
for v in ["none", "fillProportionally", "fitProportionally", "fitContentToFrame", "centerContent"] {
if ui.selectable_label(cur == v, label(v)).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, label(v))).clicked() {
d.fields.insert("fitting".into(), json!(v));
}
}
@@ -1220,12 +1246,12 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Crop Amount")).font(semibold(12.0)));
egui::Grid::new("ff_crop").num_columns(4).spacing([8.0, 6.0]).show(ui, |ui| {
for (k, l) in [("cropTop", "Top:"), ("cropLeft", "Left:")] {
ui.label(l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
text_field(ui, &mut d, k, 70.0);
}
ui.end_row();
for (k, l) in [("cropBottom", "Bottom:"), ("cropRight", "Right:")] {
ui.label(l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
text_field(ui, &mut d, k, 70.0);
}
ui.end_row();
@@ -1242,9 +1268,9 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
"vcard" => crate::i18n::tr(&app.ui.language, "Business Card"),
_ => crate::i18n::tr(&app.ui.language, "Web Hyperlink"),
};
egui::ComboBox::from_id_salt("qr_type").selected_text(label(&cur)).width(160.0).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt("qr_type").selected_text(crate::rtl::widget(ui, label(&cur))).width(160.0).show_ui(ui, |ui| {
for v in ["url", "text", "sms", "email", "vcard"] {
if ui.selectable_label(cur == v, label(v)).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, label(v))).clicked() {
d.fields.insert("type".into(), json!(v));
}
}
@@ -1259,7 +1285,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
};
egui::Grid::new("qr_fields").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
for (k, l) in fields {
ui.label(*l);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, l));
text_field(ui, &mut d, k, 260.0);
ui.end_row();
}
@@ -1290,10 +1316,10 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
}
"ruby" => {
ui.horizontal(|ui| {
ui.label("Ruby:");
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Ruby:"));
text_field(ui, &mut d, "text", 200.0);
});
ui.label(egui::RichText::new("Set over the selected text; empty removes it.").size(11.0));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Set over the selected text; empty removes it.")).size(11.0));
}
"insertTable" => {
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Table Dimensions")).font(semibold(12.0)));
@@ -1323,9 +1349,9 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Vertical justification"));
let cur = d.s("verticalJustification");
egui::ComboBox::from_id_salt("vj").selected_text(&cur).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt("vj").selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &cur))).show_ui(ui, |ui| {
for v in ["top", "center", "bottom", "justify"] {
if ui.selectable_label(cur == v, v).clicked() {
if ui.selectable_label(cur == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, v))).clicked() {
d.fields.insert("verticalJustification".into(), json!(v));
}
}
@@ -1710,7 +1736,7 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
("color", "Character Color"),
("export", "Export Tagging"),
] {
if ui.selectable_label(d.s("section") == id, label).clicked() {
if ui.selectable_label(d.s("section") == id, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
d.fields.insert("section".into(), json!(id));
}
}
@@ -1728,13 +1754,16 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Tag:"));
let cur = d.s("x.tag");
egui::ComboBox::from_id_salt("export_tag").selected_text(&cur).show_ui(ui, |ui| {
for t in ["[Automatic]", "p", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "pre", "li", "figcaption", "aside", "div"] {
if ui.selectable_label(cur == t, t).clicked() {
d.fields.insert("x.tag".into(), json!(t));
egui::ComboBox::from_id_salt("export_tag")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &cur)))
.show_ui(ui, |ui| {
for t in ["[Automatic]", "p", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote", "pre", "li", "figcaption", "aside", "div"]
{
if ui.selectable_label(cur == t, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, t))).clicked() {
d.fields.insert("x.tag".into(), json!(t));
}
}
}
});
});
});
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Class:"));
@@ -1749,42 +1778,51 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
for (i, ns) in list.iter_mut().enumerate() {
ui.horizontal(|ui| {
let style = ns["style"].as_str().unwrap_or("[None]").to_string();
egui::ComboBox::from_id_salt(("ns_style", i)).selected_text(&style).width(110.0).show_ui(ui, |ui| {
for c in &cnames {
if ui.selectable_label(*c == style, c).clicked() {
ns["style"] = json!(c);
changed = true;
egui::ComboBox::from_id_salt(("ns_style", i))
.selected_text(crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, &style)))
.width(110.0)
.show_ui(ui, |ui| {
for c in &cnames {
if ui
.selectable_label(*c == style, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, c)))
.clicked()
{
ns["style"] = json!(c);
changed = true;
}
}
}
});
});
let through = ns["through"].as_bool().unwrap_or(true);
egui::ComboBox::from_id_salt(("ns_thr", i)).selected_text(if through { "through" } else { "up to" }).width(70.0).show_ui(
ui,
|ui| {
egui::ComboBox::from_id_salt(("ns_thr", i))
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, if through { "through" } else { "up to" })))
.width(70.0)
.show_ui(ui, |ui| {
for (v, l) in [(true, "through"), (false, "up to")] {
if ui.selectable_label(v == through, l).clicked() {
if ui.selectable_label(v == through, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
ns["through"] = json!(v);
changed = true;
}
}
},
);
});
let mut n = ns["count"].as_u64().unwrap_or(1) as u32;
if ui.add(egui::DragValue::new(&mut n).range(1..=999)).changed() {
ns["count"] = json!(n);
changed = true;
}
let kind = ns["until"]["kind"].as_str().unwrap_or("words").to_string();
egui::ComboBox::from_id_salt(("ns_until", i)).selected_text(&kind).width(100.0).show_ui(ui, |ui| {
for k in
["sentences", "words", "characters", "letters", "digits", "tab", "forcedLineBreak", "emSpace", "enSpace", "chars"]
{
if ui.selectable_label(k == kind, k).clicked() {
ns["until"] = if k == "chars" { json!({"kind": "chars", "chars": ":"}) } else { json!({"kind": k}) };
changed = true;
egui::ComboBox::from_id_salt(("ns_until", i))
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &kind)))
.width(100.0)
.show_ui(ui, |ui| {
for k in
["sentences", "words", "characters", "letters", "digits", "tab", "forcedLineBreak", "emSpace", "enSpace", "chars"]
{
if ui.selectable_label(k == kind, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, k))).clicked() {
ns["until"] = if k == "chars" { json!({"kind": "chars", "chars": ":"}) } else { json!({"kind": k}) };
changed = true;
}
}
}
});
});
if kind == "chars" {
let mut c = ns["until"]["chars"].as_str().unwrap_or("").to_string();
if ui.add(egui::TextEdit::singleline(&mut c).desired_width(40.0)).changed() {
@@ -1822,21 +1860,27 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
for (i, l) in lines.iter_mut().enumerate() {
ui.horizontal(|ui| {
let style = l["style"].as_str().unwrap_or("").to_string();
egui::ComboBox::from_id_salt(("nls_style", i)).selected_text(&style).width(130.0).show_ui(ui, |ui| {
for c in &cnames {
if ui.selectable_label(*c == style, c).clicked() {
l["style"] = json!(c);
changed = true;
egui::ComboBox::from_id_salt(("nls_style", i))
.selected_text(crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, &style)))
.width(130.0)
.show_ui(ui, |ui| {
for c in &cnames {
if ui
.selectable_label(*c == style, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, c)))
.clicked()
{
l["style"] = json!(c);
changed = true;
}
}
}
});
});
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "for"));
let mut n = l["lines"].as_u64().unwrap_or(1) as u32;
if ui.add(egui::DragValue::new(&mut n).range(1..=999)).changed() {
l["lines"] = json!(n);
changed = true;
}
ui.label(if n == 1 { "line" } else { "lines" });
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, if n == 1 { "line" } else { "lines" }));
if ui
.small_button("×")
.on_hover_ui(|ui| {
@@ -1869,14 +1913,20 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Apply Style:"));
let style = g["style"].as_str().unwrap_or("").to_string();
egui::ComboBox::from_id_salt(("gs_style", i)).selected_text(&style).width(110.0).show_ui(ui, |ui| {
for c in &cnames {
if ui.selectable_label(*c == style, c).clicked() {
g["style"] = json!(c);
changed = true;
egui::ComboBox::from_id_salt(("gs_style", i))
.selected_text(crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, &style)))
.width(110.0)
.show_ui(ui, |ui| {
for c in &cnames {
if ui
.selectable_label(*c == style, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, c)))
.clicked()
{
g["style"] = json!(c);
changed = true;
}
}
}
});
});
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "To Text:"));
let mut pat = g["pattern"].as_str().unwrap_or("").to_string();
let bad = regex_ok(&pat).is_err();
@@ -1935,7 +1985,7 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
});
ui.end_row();
for (label, key, suffix) in [("Size:", "size", " pt"), ("Tracking:", "tracking", "")] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let v = cur(d, &format!("c.{key}"), &cv[key]).as_f64();
if let Some(n) = crate::widgets::number(ui, &format!("ps{key}"), v, suffix, 80.0, 2) {
d.fields.insert(format!("c.{key}"), json!(n));
@@ -1957,13 +2007,16 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
egui::Grid::new("psi").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Alignment:"));
let a: designcraft_doc::Align = serde_json::from_value(cur(d, "p.align", &pv["align"])).unwrap_or_default();
egui::ComboBox::from_id_salt("psalign").selected_text(a.label()).width(240.0).show_ui(ui, |ui| {
for al in designcraft_doc::Align::ALL {
if ui.selectable_label(al == a, al.label()).clicked() {
d.fields.insert("p.align".into(), serde_json::to_value(al).unwrap_or_default());
egui::ComboBox::from_id_salt("psalign")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, a.label())))
.width(240.0)
.show_ui(ui, |ui| {
for al in designcraft_doc::Align::ALL {
if ui.selectable_label(al == a, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, al.label()))).clicked() {
d.fields.insert("p.align".into(), serde_json::to_value(al).unwrap_or_default());
}
}
}
});
});
ui.end_row();
for (label, key) in [
("Left Indent:", "leftIndent"),
@@ -1972,7 +2025,7 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
("Space Before:", "spaceBefore"),
("Space After:", "spaceAfter"),
] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let v = cur(d, &format!("p.{key}"), &pv[key]).as_f64();
if let Some(n) = crate::widgets::measure(ui, &format!("ps{key}"), v, units, 80.0) {
d.fields.insert(format!("p.{key}"), json!(n));
@@ -1993,7 +2046,7 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
("Before Last:", "hyphBeforeLast"),
("Hyphen Limit:", "hyphLimit"),
] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
let v = cur(d, &format!("p.{key}"), &pv[key]).as_f64();
if let Some(n) = crate::widgets::number(ui, &format!("ps{key}"), v, "", 60.0, 0) {
d.fields.insert(format!("p.{key}"), json!(n.max(0.0) as u64));
@@ -2010,11 +2063,11 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Maximum"));
ui.end_row();
for (label, base) in [("Word Spacing:", "wordSpace"), ("Letter Spacing:", "letterSpace"), ("Glyph Scaling:", "glyphScale")] {
crate::rtl::label(ui, label);
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, label));
for suffix in ["Min", "Desired", "Max"] {
let key = format!("{base}{suffix}");
let v = cur(d, &format!("p.{key}"), &pv[key.as_str()]).as_f64().map(|x| x * 100.0);
if let Some(n) = crate::widgets::number(ui, &format!("ps{key}"), v, "%", 60.0, 0) {
if let Some(n) = ui.scope(|ui| crate::widgets::number(ui, &format!("ps{key}"), v, "%", 60.0, 0)).inner {
d.fields.insert(format!("p.{key}"), json!(n / 100.0));
}
}
@@ -2029,9 +2082,9 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
d.fields.insert("p.composer".into(), json!("singleLine"));
}
ui.end_row();
ui.label("Insert Kashidas:");
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Insert Kashidas:"));
let mut k = cur(d, "p.kashidas", &pv["kashidas"]).as_bool().unwrap_or(true);
if ui.checkbox(&mut k, "In justified Arabic text").changed() {
if ui.checkbox(&mut k, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "In justified Arabic text"))).changed() {
d.fields.insert("p.kashidas".into(), json!(k));
}
ui.end_row();
@@ -2072,19 +2125,29 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
.map(str::to_string)
.or(style.based_on.clone())
.unwrap_or_else(|| "[No Paragraph Style]".into());
egui::ComboBox::from_id_salt("psbased").selected_text(&based).width(220.0).show_ui(ui, |ui| {
for n in &names {
if ui.selectable_label(*n == based, n).clicked() {
d.fields.insert("basedOn".into(), json!(n));
egui::ComboBox::from_id_salt("psbased")
.selected_text(crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, &based)))
.width(220.0)
.show_ui(ui, |ui| {
for n in &names {
if ui.selectable_label(*n == based, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, n))).clicked() {
d.fields.insert("basedOn".into(), json!(n));
}
}
}
});
});
ui.end_row();
});
ui.add_space(8.0);
ui.label(
egui::RichText::new(format!("{} {} {:.1} pt · {}", cp.font_family, cp.font_style, cp.size, pp.align.label()))
.color(crate::theme::Tokens::get(ui.ctx()).text_dim),
crate::rtl::label(
ui,
egui::RichText::new(format!(
"{} {} {:.1} pt · {}",
cp.font_family,
cp.font_style,
cp.size,
crate::i18n::tr(&app.ui.language, pp.align.label())
))
.color(crate::theme::Tokens::get(ui.ctx()).text_dim),
);
}
});
@@ -2094,15 +2157,21 @@ fn paragraph_style_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialo
fn combo(ui: &mut egui::Ui, d: &mut Dialog, key: &str, opts: &[(&str, &str)]) {
let cur = d.s(key);
let shown = opts.iter().find(|o| o.0 == cur).map_or(cur.as_str(), |o| o.1).to_string();
egui::ComboBox::from_id_salt(key).selected_text(shown).width(170.0).show_ui(ui, |ui| {
egui::ComboBox::from_id_salt(key).selected_text(crate::rtl::widget(ui, &shown)).width(170.0).show_ui(ui, |ui| {
for (v, label) in opts {
if ui.selectable_label(cur == *v, *label).clicked() {
if ui.selectable_label(cur == *v, crate::rtl::widget(ui, *label)).clicked() {
d.fields.insert(key.into(), json!(v));
}
}
});
}
/// Translate enum captions without changing serialized values or user-defined names.
fn translated_combo(lang: &str, ui: &mut egui::Ui, d: &mut Dialog, key: &str, opts: &[(&str, &str)]) {
let opts: Vec<_> = opts.iter().map(|(value, label)| (*value, crate::i18n::tr(lang, label))).collect();
combo(ui, d, key, &opts);
}
/// Style name picker (paragraph or character styles of the active document).
fn style_combo(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog, key: &str, character: bool) {
let names: Vec<String> = app
@@ -2116,9 +2185,9 @@ fn style_combo(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog, key: &str, ch
}
})
.unwrap_or_default();
let mut opts: Vec<(&str, &str)> = names.iter().map(|n| (n.as_str(), n.as_str())).collect();
let mut opts: Vec<(&str, &str)> = names.iter().map(|n| (n.as_str(), crate::i18n::style_name(&app.ui.language, n))).collect();
if character && !names.iter().any(|n| n == designcraft_doc::NO_CHAR_STYLE) {
opts.insert(0, (designcraft_doc::NO_CHAR_STYLE, designcraft_doc::NO_CHAR_STYLE));
opts.insert(0, (designcraft_doc::NO_CHAR_STYLE, crate::i18n::style_name(&app.ui.language, designcraft_doc::NO_CHAR_STYLE)));
}
combo(ui, d, key, &opts);
}
@@ -2127,7 +2196,7 @@ fn style_combo(app: &DesignApp, ui: &mut egui::Ui, d: &mut Dialog, key: &str, ch
fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
ui.horizontal(|ui| {
for (tab, label) in [("numbering", "Numbering and Formatting"), ("layout", "Layout")] {
if ui.selectable_label(d.s("tab") == tab, label).clicked() {
if ui.selectable_label(d.s("tab") == tab, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
d.fields.insert("tab".into(), json!(tab));
}
}
@@ -2135,7 +2204,7 @@ fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
ui.separator();
let head = |ui: &mut egui::Ui, t: &str| {
ui.add_space(4.0);
ui.label(egui::RichText::new(t).font(semibold(12.0)));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, t)).font(semibold(12.0)));
};
if d.s("tab") == "layout" {
head(ui, "Spacing Options");
@@ -2150,7 +2219,8 @@ fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
head(ui, "First Baseline");
egui::Grid::new("fn_fb").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Offset:"));
combo(
translated_combo(
&app.ui.language,
ui,
d,
"firstBaseline",
@@ -2186,7 +2256,8 @@ fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
head(ui, "Numbering");
egui::Grid::new("fn_num").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Style:"));
combo(
translated_combo(
&app.ui.language,
ui,
d,
"style",
@@ -2205,10 +2276,22 @@ fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
text_field(ui, d, "startAt", 60.0);
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Restart Numbering Every:"));
combo(ui, d, "restart", &[("never", "Never (continuous)"), ("page", "Page"), ("spread", "Spread"), ("section", "Section")]);
translated_combo(
&app.ui.language,
ui,
d,
"restart",
&[("never", "Never (continuous)"), ("page", "Page"), ("spread", "Spread"), ("section", "Section")],
);
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Show Prefix/Suffix in:"));
combo(ui, d, "affixIn", &[("none", "None"), ("reference", "Footnote Reference"), ("text", "Footnote Text"), ("both", "Both")]);
translated_combo(
&app.ui.language,
ui,
d,
"affixIn",
&[("none", "None"), ("reference", "Footnote Reference"), ("text", "Footnote Text"), ("both", "Both")],
);
ui.end_row();
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Prefix:"));
text_field(ui, d, "prefix", 60.0);
@@ -2220,7 +2303,8 @@ fn footnote_options(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
head(ui, "Footnote Reference Number in Text");
egui::Grid::new("fn_ref").num_columns(2).spacing([8.0, 6.0]).show(ui, |ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Position:"));
combo(
translated_combo(
&app.ui.language,
ui,
d,
"refPosition",
@@ -2345,7 +2429,8 @@ fn command_form(app: &crate::DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
check(ui, d, &f.key, "");
} else if !f.choices.is_empty() {
let mut opts: Vec<(&str, &str)> = vec![("", "—")];
opts.extend(f.choices.iter().map(|c| (c.as_str(), c.as_str())));
let labels: Vec<_> = f.choices.iter().map(|c| humanize(c)).collect();
opts.extend(f.choices.iter().zip(&labels).map(|(value, label)| (value.as_str(), crate::i18n::tr(&app.ui.language, label))));
combo(ui, d, &f.key, &opts);
} else {
let mut s = d.s(&f.key);
@@ -2499,7 +2584,12 @@ fn find_font(app: &mut DesignApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
let fonts = d.fields.get("_fonts").and_then(Value::as_array).cloned().unwrap_or_default();
let missing = fonts.iter().filter(|f| f["missing"] == true || f["styleMissing"] == true).count();
ui.label(format!("Fonts in Document: {} Missing: {missing}", fonts.len()));
crate::rtl::label(
ui,
crate::i18n::tr(&app.ui.language, "Fonts in Document: {count} Missing: {missing}")
.replace("{count}", &fonts.len().to_string())
.replace("{missing}", &missing.to_string()),
);
egui::ScrollArea::vertical().id_salt("ff_list").max_height(180.0).show(ui, |ui| {
for f in &fonts {
let (fam, st) = (f["family"].as_str().unwrap_or(""), f["style"].as_str().unwrap_or(""));
+3 -1
View File
@@ -132,9 +132,11 @@ pub fn flyout(app: &mut DesignApp, ctx: &egui::Context) {
egui::Area::new(egui::Id::new("panel_flyout")).order(egui::Order::Foreground).fixed_pos(pos).show(ctx, |ui| {
egui::Frame::popup(ui.style()).fill(t.panel).inner_margin(egui::Margin::same(0)).show(ui, |ui| {
ui.set_width(256.0);
// A previous shorter flyout must not constrain the next panel's scroll viewport.
ui.set_max_height((screen.max.y - pos.y - 12.0).max(80.0));
let (strip, _) = ui.allocate_exact_size(vec2(256.0, 26.0), Sense::hover());
ui.painter().rect_filled(strip, 0.0, t.panel_darker);
ui.painter().text(strip.min + vec2(10.0, 13.0), egui::Align2::LEFT_CENTER, label, semibold(12.0), t.text_strong);
crate::rtl::paint(ui.painter(), strip.min + vec2(10.0, 13.0), egui::Align2::LEFT_CENTER, &label, semibold(12.0), t.text_strong);
let close = egui::Rect::from_min_size(egui::pos2(strip.max.x - 22.0, strip.min.y + 4.0), vec2(18.0, 18.0));
if ui.interact(close, ui.id().with("flyclose"), Sense::click()).clicked() {
open = false;
+829 -3
View File
@@ -204,7 +204,7 @@ const TABLE: &[(&str, [&str; 5])] = &[
("Color Settings", ["Color Settings", "Color Settings", "Color Settings", "Color Settings", "颜色设置"]),
("Color:", ["Color:", "Color:", "Color:", "Color:", "颜色:"]),
("Columns", ["Columns", "Columns", "Columns", "Columns", "栏"]),
("Composer:", ["Composer:", "Composer:", "Composer:", "Composer:", "书写器:"]),
("Composer:", ["Composer:", "Composer:", "Composer:", "Composer:", "排版器:"]),
("Copies:", ["Copies:", "Copies:", "Copies:", "Copies:", "份数:"]),
("Count", ["Count", "Count", "Count", "Count", "数量"]),
("Create", ["Create", "Create", "Create", "Create", "创建"]),
@@ -362,7 +362,7 @@ const TABLE: &[(&str, [&str; 5])] = &[
("Orientation:", ["Orientation:", "Orientation:", "Orientation:", "Orientation:", "方向:"]),
("Outside", ["Outside", "Outside", "Outside", "Outside", "外侧"]),
("Pages:", ["Pages:", "Pages:", "Pages:", "Pages:", "页面:"]),
("Paragraph Composer", ["Paragraph Composer", "Paragraph Composer", "Paragraph Composer", "Paragraph Composer", "段落书写器"]),
("Paragraph Composer", ["Paragraph Composer", "Paragraph Composer", "Paragraph Composer", "Paragraph Composer", "段落排版器"]),
(
"Paragraph Style Options",
["Paragraph Style Options", "Paragraph Style Options", "Paragraph Style Options", "Paragraph Style Options", "段落样式选项"],
@@ -442,7 +442,7 @@ const TABLE: &[(&str, [&str; 5])] = &[
),
("Show Tag Markers", ["Show Tag Markers", "Show Tag Markers", "Show Tag Markers", "Show Tag Markers", "显示标记的框架"]),
("Single-line", ["Single-line", "Single-line", "Single-line", "Single-line", "单行"]),
("Single-line Composer", ["Single-line Composer", "Single-line Composer", "Single-line Composer", "Single-line Composer", "单行书写器"]),
("Single-line Composer", ["Single-line Composer", "Single-line Composer", "Single-line Composer", "Single-line Composer", "单行排版器"]),
("Size:", ["Size:", "Size:", "Size:", "Size:", "大小:"]),
(
"Space Between Footnotes:",
@@ -1786,6 +1786,816 @@ const TABLE: &[(&str, [&str; 5])] = &[
["⌥-click clears overrides", "⌥-click clears overrides", "⌥-click clears overrides", "⌥-click clears overrides", "⌥-单击清除覆盖"],
),
("⚠ out of date", ["⚠ out of date", "⚠ out of date", "⚠ out of date", "⚠ out of date", "⚠ 已过期"]),
("General", ["General", "General", "General", "General", "常规"]),
(
"Basic Character Formats",
["Basic Character Formats", "Basic Character Formats", "Basic Character Formats", "Basic Character Formats", "基本字符格式"],
),
("Indents and Spacing", ["Indents and Spacing", "Indents and Spacing", "Indents and Spacing", "Indents and Spacing", "缩进和间距"]),
("Hyphenation", ["Hyphenation", "Hyphenation", "Hyphenation", "Hyphenation", "断字"]),
("Justification", ["Justification", "Justification", "Justification", "Justification", "两端对齐"]),
(
"Drop Caps and Nested Styles",
[
"Drop Caps and Nested Styles",
"Drop Caps and Nested Styles",
"Drop Caps and Nested Styles",
"Drop Caps and Nested Styles",
"首字下沉和嵌套样式",
],
),
("GREP Style", ["GREP Style", "GREP Style", "GREP Style", "GREP Style", "GREP 样式"]),
("Character Color", ["Character Color", "Character Color", "Character Color", "Character Color", "字符颜色"]),
(
"Numbering and Formatting",
["Numbering and Formatting", "Numbering and Formatting", "Numbering and Formatting", "Numbering and Formatting", "编号和格式"],
),
("Spacing Options", ["Spacing Options", "Spacing Options", "Spacing Options", "Spacing Options", "间距选项"]),
("First Baseline", ["First Baseline", "First Baseline", "First Baseline", "First Baseline", "首行基线"]),
("Rule Above", ["Rule Above", "Rule Above", "Rule Above", "Rule Above", "上方横线"]),
("Numbering", ["Numbering", "Numbering", "Numbering", "Numbering", "编号"]),
(
"Footnote Reference Number in Text",
[
"Footnote Reference Number in Text",
"Footnote Reference Number in Text",
"Footnote Reference Number in Text",
"Footnote Reference Number in Text",
"正文中的脚注引用编号",
],
),
("Footnote Formatting", ["Footnote Formatting", "Footnote Formatting", "Footnote Formatting", "Footnote Formatting", "脚注格式"]),
(
"Span Footnotes Across Columns",
[
"Span Footnotes Across Columns",
"Span Footnotes Across Columns",
"Span Footnotes Across Columns",
"Span Footnotes Across Columns",
"脚注跨栏",
],
),
("Rule On", ["Rule On", "Rule On", "Rule On", "Rule On", "启用横线"]),
("Ascent", ["Ascent", "Ascent", "Ascent", "Ascent", "升部"]),
("Cap Height", ["Cap Height", "Cap Height", "Cap Height", "Cap Height", "大写字母高度"]),
("Leading", ["Leading", "Leading", "Leading", "Leading", "行距"]),
("x Height", ["x Height", "x Height", "x Height", "x Height", "x 字高"]),
("Fixed", ["Fixed", "Fixed", "Fixed", "Fixed", "固定"]),
("Never (continuous)", ["Never (continuous)", "Never (continuous)", "Never (continuous)", "Never (continuous)", "从不(连续编号)"]),
("Page", ["Page", "Page", "Page", "Page", "页面"]),
("Spread", ["Spread", "Spread", "Spread", "Spread", "跨页"]),
("Section", ["Section", "Section", "Section", "Section", "章节"]),
("Footnote Reference", ["Footnote Reference", "Footnote Reference", "Footnote Reference", "Footnote Reference", "脚注引用"]),
("Footnote Text", ["Footnote Text", "Footnote Text", "Footnote Text", "Footnote Text", "脚注文本"]),
("Both", ["Both", "Both", "Both", "Both", "两者"]),
("Apply Superscript", ["Apply Superscript", "Apply Superscript", "Apply Superscript", "Apply Superscript", "应用上标"]),
("Apply Subscript", ["Apply Subscript", "Apply Subscript", "Apply Subscript", "Apply Subscript", "应用下标"]),
("Apply Normal", ["Apply Normal", "Apply Normal", "Apply Normal", "Apply Normal", "应用正常位置"]),
("OpenType Superscript", ["OpenType Superscript", "OpenType Superscript", "OpenType Superscript", "OpenType Superscript", "OpenType 上标"]),
("through", ["through", "through", "through", "through", "直到(包含)"]),
("up to", ["up to", "up to", "up to", "up to", "直到(不包含)"]),
("line", ["line", "line", "line", "line", "行"]),
("lines", ["lines", "lines", "lines", "lines", "行"]),
("sentences", ["sentences", "sentences", "sentences", "sentences", "句子"]),
("words", ["words", "words", "words", "words", "单词"]),
("characters", ["characters", "characters", "characters", "characters", "字符"]),
("letters", ["letters", "letters", "letters", "letters", "字母"]),
("digits", ["digits", "digits", "digits", "digits", "数字"]),
("tab", ["tab", "tab", "tab", "tab", "制表符"]),
("forcedLineBreak", ["forcedLineBreak", "forcedLineBreak", "forcedLineBreak", "forcedLineBreak", "强制换行符"]),
("emSpace", ["emSpace", "emSpace", "emSpace", "emSpace", "全角空格"]),
("enSpace", ["enSpace", "enSpace", "enSpace", "enSpace", "半角空格"]),
("chars", ["chars", "chars", "chars", "chars", "指定字符"]),
("[Automatic]", ["[Automatic]", "[Automatic]", "[Automatic]", "[Automatic]", "[自动]"]),
("Tracking:", ["Tracking:", "Tracking:", "Tracking:", "Tracking:", "字距:"]),
("Right Indent:", ["Right Indent:", "Right Indent:", "Right Indent:", "Right Indent:", "右缩进:"]),
("First Line Indent:", ["First Line Indent:", "First Line Indent:", "First Line Indent:", "First Line Indent:", "首行缩进:"]),
("Space Before:", ["Space Before:", "Space Before:", "Space Before:", "Space Before:", "段前间距:"]),
("Space After:", ["Space After:", "Space After:", "Space After:", "Space After:", "段后间距:"]),
("Words with at Least:", ["Words with at Least:", "Words with at Least:", "Words with at Least:", "Words with at Least:", "最少单词长度:"]),
("After First:", ["After First:", "After First:", "After First:", "After First:", "前部最少保留:"]),
("Before Last:", ["Before Last:", "Before Last:", "Before Last:", "Before Last:", "后部最少保留:"]),
("Hyphen Limit:", ["Hyphen Limit:", "Hyphen Limit:", "Hyphen Limit:", "Hyphen Limit:", "连续断字上限:"]),
("Word Spacing:", ["Word Spacing:", "Word Spacing:", "Word Spacing:", "Word Spacing:", "单词间距:"]),
("Letter Spacing:", ["Letter Spacing:", "Letter Spacing:", "Letter Spacing:", "Letter Spacing:", "字母间距:"]),
("Glyph Scaling:", ["Glyph Scaling:", "Glyph Scaling:", "Glyph Scaling:", "Glyph Scaling:", "字形缩放:"]),
("Insert Kashidas:", ["Insert Kashidas:", "Insert Kashidas:", "Insert Kashidas:", "Insert Kashidas:", "插入延长线:"]),
(
"In justified Arabic text",
[
"In justified Arabic text",
"In justified Arabic text",
"In justified Arabic text",
"In justified Arabic text",
"在两端对齐的阿拉伯语文本中",
],
),
("Binding:", ["Binding:", "Binding:", "Binding:", "Binding:", "装订方向:"]),
("Ruby:", ["Ruby:", "Ruby:", "Ruby:", "Ruby:", "注音:"]),
(
"Set over the selected text; empty removes it.",
[
"Set over the selected text; empty removes it.",
"Set over the selected text; empty removes it.",
"Set over the selected text; empty removes it.",
"Set over the selected text; empty removes it.",
"在所选文本上方添加注音;留空可移除。",
],
),
("[Basic Paragraph]", ["[Basic Paragraph]", "[Basic Paragraph]", "[Basic Paragraph]", "[Basic Paragraph]", "[基本段落]"]),
("[No Paragraph Style]", ["[No Paragraph Style]", "[No Paragraph Style]", "[No Paragraph Style]", "[No Paragraph Style]", "[无段落样式]"]),
("Align left", ["Align left", "Align left", "Align left", "Align left", "左对齐"]),
("Align center", ["Align center", "Align center", "Align center", "Align center", "居中对齐"]),
("Align right", ["Align right", "Align right", "Align right", "Align right", "右对齐"]),
(
"Justify with last line aligned left",
[
"Justify with last line aligned left",
"Justify with last line aligned left",
"Justify with last line aligned left",
"Justify with last line aligned left",
"两端对齐,末行左对齐",
],
),
(
"Justify with last line aligned center",
[
"Justify with last line aligned center",
"Justify with last line aligned center",
"Justify with last line aligned center",
"Justify with last line aligned center",
"两端对齐,末行居中",
],
),
(
"Justify with last line aligned right",
[
"Justify with last line aligned right",
"Justify with last line aligned right",
"Justify with last line aligned right",
"Justify with last line aligned right",
"两端对齐,末行右对齐",
],
),
("Justify all lines", ["Justify all lines", "Justify all lines", "Justify all lines", "Justify all lines", "所有行两端对齐"]),
("Align towards spine", ["Align towards spine", "Align towards spine", "Align towards spine", "Align towards spine", "朝向书脊对齐"]),
("Align away from spine", ["Align away from spine", "Align away from spine", "Align away from spine", "Align away from spine", "背向书脊对齐"]),
(
"Create New Style from Selection",
[
"Create New Style from Selection",
"Create New Style from Selection",
"Create New Style from Selection",
"Create New Style from Selection",
"从所选内容创建新样式",
],
),
("No text selected", ["No text selected", "No text selected", "No text selected", "No text selected", "未选择文本"]),
("Search", ["Search", "Search", "Search", "Search", "搜索"]),
("Essentials", ["Essentials", "Essentials", "Essentials", "Essentials", "基本功能"]),
("Advanced", ["Advanced", "Advanced", "Advanced", "Advanced", "高级"]),
("Digital Publishing", ["Digital Publishing", "Digital Publishing", "Digital Publishing", "Digital Publishing", "数字出版"]),
("Interactive for PDF", ["Interactive for PDF", "Interactive for PDF", "Interactive for PDF", "Interactive for PDF", "PDF 交互"]),
("Printing and Proofing", ["Printing and Proofing", "Printing and Proofing", "Printing and Proofing", "Printing and Proofing", "打印和校样"]),
("Typography", ["Typography", "Typography", "Typography", "Typography", "文字排版"]),
("Rulers & Grids", ["Rulers & Grids", "Rulers & Grids", "Rulers & Grids", "Rulers & Grids", "标尺和网格"]),
("No errors", ["No errors", "No errors", "No errors", "No errors", "无错误"]),
("error", ["error", "error", "error", "error", "个错误"]),
("errors", ["errors", "errors", "errors", "errors", "个错误"]),
("Reset", ["Reset", "Reset", "Reset", "Reset", "重置"]),
("Right to Left", ["Right to Left", "Right to Left", "Right to Left", "Right to Left", "从右向左"]),
("Left to Right", ["Left to Right", "Left to Right", "Left to Right", "Left to Right", "从左向右"]),
("Portrait", ["Portrait", "Portrait", "Portrait", "Portrait", "纵向"]),
("Landscape", ["Landscape", "Landscape", "Landscape", "Landscape", "横向"]),
(
"Display All Blacks as Rich Black",
[
"Display All Blacks as Rich Black",
"Display All Blacks as Rich Black",
"Display All Blacks as Rich Black",
"Display All Blacks as Rich Black",
"所有黑色显示为浓黑",
],
),
(
"Display All Blacks Accurately",
[
"Display All Blacks Accurately",
"Display All Blacks Accurately",
"Display All Blacks Accurately",
"Display All Blacks Accurately",
"准确显示所有黑色",
],
),
(
"Output All Blacks as Rich Black",
[
"Output All Blacks as Rich Black",
"Output All Blacks as Rich Black",
"Output All Blacks as Rich Black",
"Output All Blacks as Rich Black",
"所有黑色输出为浓黑",
],
),
(
"Output All Blacks Accurately",
[
"Output All Blacks Accurately",
"Output All Blacks Accurately",
"Output All Blacks Accurately",
"Output All Blacks Accurately",
"准确输出所有黑色",
],
),
("Top Margin", ["Top Margin", "Top Margin", "Top Margin", "Top Margin", "上边距"]),
("Top of Page", ["Top of Page", "Top of Page", "Top of Page", "Top of Page", "页面顶部"]),
(
"Paragraph Styles panel",
["Paragraph Styles panel", "Paragraph Styles panel", "Paragraph Styles panel", "Paragraph Styles panel", "段落样式面板"],
),
(
"Character Styles panel",
["Character Styles panel", "Character Styles panel", "Character Styles panel", "Character Styles panel", "字符样式面板"],
),
("Create new style", ["Create new style", "Create new style", "Create new style", "Create new style", "创建新样式"]),
("Clear overrides", ["Clear overrides", "Clear overrides", "Clear overrides", "Clear overrides", "清除覆盖"]),
("Apply", ["Apply", "Apply", "Apply", "Apply", "应用"]),
("Accept", ["Accept", "Accept", "Accept", "Accept", "接受"]),
("Reject", ["Reject", "Reject", "Reject", "Reject", "拒绝"]),
("Add Selection", ["Add Selection", "Add Selection", "Add Selection", "Add Selection", "添加所选内容"]),
("Align Stroke:", ["Align Stroke:", "Align Stroke:", "Align Stroke:", "Align Stroke:", "描边对齐:"]),
("Angle", ["Angle", "Angle", "Angle", "Angle", "角度"]),
("Angle:", ["Angle:", "Angle:", "Angle:", "Angle:", "角度:"]),
("Baseline", ["Baseline", "Baseline", "Baseline", "Baseline", "基线"]),
("Cap:", ["Cap:", "Cap:", "Cap:", "Cap:", "端点:"]),
("Cell Text", ["Cell Text", "Cell Text", "Cell Text", "Cell Text", "单元格文本"]),
("Cell Type", ["Cell Type", "Cell Type", "Cell Type", "Cell Type", "单元格类型"]),
("Composer", ["Composer", "Composer", "Composer", "Composer", "排版器"]),
("Convert to Text", ["Convert to Text", "Convert to Text", "Convert to Text", "Convert to Text", "转换为文本"]),
("Create New Layer", ["Create New Layer", "Create New Layer", "Create New Layer", "Create New Layer", "创建新图层"]),
("Delete DTD", ["Delete DTD", "Delete DTD", "Delete DTD", "Delete DTD", "删除 DTD"]),
("Digits", ["Digits", "Digits", "Digits", "Digits", "数字"]),
("Direction", ["Direction", "Direction", "Direction", "Direction", "方向"]),
("Distance", ["Distance", "Distance", "Distance", "Distance", "距离"]),
("Distribute ↔", ["Distribute ↔", "Distribute ↔", "Distribute ↔", "Distribute ↔", "水平分布"]),
("Distribute ↕", ["Distribute ↕", "Distribute ↕", "Distribute ↕", "Distribute ↕", "垂直分布"]),
("Effects: Drop Shadow", ["Effects: Drop Shadow", "Effects: Drop Shadow", "Effects: Drop Shadow", "Effects: Drop Shadow", "效果:投影"]),
("Favorites", ["Favorites", "Favorites", "Favorites", "Favorites", "收藏"]),
("First Spread", ["First Spread", "First Spread", "First Spread", "First Spread", "第一个跨页"]),
("Gap Color:", ["Gap Color:", "Gap Color:", "Gap Color:", "Gap Color:", "间隙颜色:"]),
("Graphic…", ["Graphic…", "Graphic…", "Graphic…", "Graphic…", "图形…"]),
("Home", ["Home", "Home", "Home", "Home", "主页"]),
("Join:", ["Join:", "Join:", "Join:", "Join:", "连接:"]),
("Language", ["Language", "Language", "Language", "Language", "语言"]),
("Last Spread", ["Last Spread", "Last Spread", "Last Spread", "Last Spread", "最后一个跨页"]),
("Load DTD…", ["Load DTD…", "Load DTD…", "Load DTD…", "Load DTD…", "载入 DTD…"]),
("Location:", ["Location:", "Location:", "Location:", "Location:", "位置:"]),
("Lock Guides", ["Lock Guides", "Lock Guides", "Lock Guides", "Lock Guides", "锁定参考线"]),
("Miter Limit:", ["Miter Limit:", "Miter Limit:", "Miter Limit:", "Miter Limit:", "斜接限制:"]),
(
"New Article from Selection",
[
"New Article from Selection",
"New Article from Selection",
"New Article from Selection",
"New Article from Selection",
"从所选内容新建文章",
],
),
("New Note at Cursor", ["New Note at Cursor", "New Note at Cursor", "New Note at Cursor", "New Note at Cursor", "在光标处新建附注"]),
("New Swatch", ["New Swatch", "New Swatch", "New Swatch", "New Swatch", "新建色板"]),
("New condition name", ["New condition name", "New condition name", "New condition name", "New condition name", "新条件名称"]),
("New group…", ["New group…", "New group…", "New group…", "New group…", "新建组…"]),
("New tag", ["New tag", "New tag", "New tag", "New tag", "新建标签"]),
("Next Spread", ["Next Spread", "Next Spread", "Next Spread", "Next Spread", "下一个跨页"]),
("Note text", ["Note text", "Note text", "Note text", "Note text", "附注文本"]),
("Offset", ["Offset", "Offset", "Offset", "Offset", "偏移"]),
("Offsets", ["Offsets", "Offsets", "Offsets", "Offsets", "偏移量"]),
("Paragraph alignment", ["Paragraph alignment", "Paragraph alignment", "Paragraph alignment", "Paragraph alignment", "段落对齐"]),
("Previous Spread", ["Previous Spread", "Previous Spread", "Previous Spread", "Previous Spread", "上一个跨页"]),
("Release to Objects", ["Release to Objects", "Release to Objects", "Release to Objects", "Release to Objects", "释放为对象"]),
("Remove", ["Remove", "Remove", "Remove", "Remove", "移除"]),
(
"Rotate 90° Counterclockwise",
[
"Rotate 90° Counterclockwise",
"Rotate 90° Counterclockwise",
"Rotate 90° Counterclockwise",
"Rotate 90° Counterclockwise",
"逆时针旋转 90°",
],
),
("Rotation Angle", ["Rotation Angle", "Rotation Angle", "Rotation Angle", "Rotation Angle", "旋转角度"]),
("Rows", ["Rows", "Rows", "Rows", "Rows", "行数"]),
("Run", ["Run", "Run", "Run", "Run", "运行"]),
("Scale X Percentage", ["Scale X Percentage", "Scale X Percentage", "Scale X Percentage", "Scale X Percentage", "水平缩放百分比"]),
("Scale Y Percentage", ["Scale Y Percentage", "Scale Y Percentage", "Scale Y Percentage", "Scale Y Percentage", "垂直缩放百分比"]),
("Search commands", ["Search commands", "Search commands", "Search commands", "Search commands", "搜索命令"]),
("Search fonts", ["Search fonts", "Search fonts", "Search fonts", "Search fonts", "搜索字体"]),
(
"Search styles and commands…",
[
"Search styles and commands…",
"Search styles and commands…",
"Search styles and commands…",
"Search styles and commands…",
"搜索样式和命令…",
],
),
(
"Search: character or U+code",
[
"Search: character or U+code",
"Search: character or U+code",
"Search: character or U+code",
"Search: character or U+code",
"搜索字符或 U+ 编码",
],
),
("Share", ["Share", "Share", "Share", "Share", "分享"]),
("Shear X Angle", ["Shear X Angle", "Shear X Angle", "Shear X Angle", "Shear X Angle", "水平倾斜角度"]),
("Show Baseline Grid", ["Show Baseline Grid", "Show Baseline Grid", "Show Baseline Grid", "Show Baseline Grid", "显示基线网格"]),
("Show Document Grid", ["Show Document Grid", "Show Document Grid", "Show Document Grid", "Show Document Grid", "显示文档网格"]),
("Show Guides", ["Show Guides", "Show Guides", "Show Guides", "Show Guides", "显示参考线"]),
("Smart Guides", ["Smart Guides", "Smart Guides", "Smart Guides", "Smart Guides", "智能参考线"]),
("Stop Color:", ["Stop Color:", "Stop Color:", "Stop Color:", "Stop Color:", "色标颜色:"]),
("Style", ["Style", "Style", "Style", "Style", "样式"]),
("Styles panel", ["Styles panel", "Styles panel", "Styles panel", "Styles panel", "样式面板"]),
("Text", ["Text", "Text", "Text", "Text", "文本"]),
(
"The structure is valid.",
["The structure is valid.", "The structure is valid.", "The structure is valid.", "The structure is valid.", "结构有效。"],
),
("Toggle double column", ["Toggle double column", "Toggle double column", "Toggle double column", "Toggle double column", "切换双列布局"]),
("Tracking", ["Tracking", "Tracking", "Tracking", "Tracking", "字距"]),
("Untag Selection", ["Untag Selection", "Untag Selection", "Untag Selection", "Untag Selection", "移除所选内容的标签"]),
("Validate", ["Validate", "Validate", "Validate", "Validate", "验证"]),
("Weight", ["Weight", "Weight", "Weight", "Weight", "粗细"]),
("Subtract", ["Subtract", "Subtract", "Subtract", "Subtract", "减去"]),
("Intersect", ["Intersect", "Intersect", "Intersect", "Intersect", "交集"]),
("Exclude Overlap", ["Exclude Overlap", "Exclude Overlap", "Exclude Overlap", "Exclude Overlap", "排除重叠"]),
("Minus Back", ["Minus Back", "Minus Back", "Minus Back", "Minus Back", "减去后方对象"]),
("Align left edges", ["Align left edges", "Align left edges", "Align left edges", "Align left edges", "左边缘对齐"]),
(
"Align horizontal centers",
["Align horizontal centers", "Align horizontal centers", "Align horizontal centers", "Align horizontal centers", "水平居中对齐"],
),
("Align right edges", ["Align right edges", "Align right edges", "Align right edges", "Align right edges", "右边缘对齐"]),
("Align top edges", ["Align top edges", "Align top edges", "Align top edges", "Align top edges", "顶边缘对齐"]),
(
"Align vertical centers",
["Align vertical centers", "Align vertical centers", "Align vertical centers", "Align vertical centers", "垂直居中对齐"],
),
("Align bottom edges", ["Align bottom edges", "Align bottom edges", "Align bottom edges", "Align bottom edges", "底边缘对齐"]),
("Align to Selection", ["Align to Selection", "Align to Selection", "Align to Selection", "Align to Selection", "对齐到所选对象"]),
("Align to Key Object", ["Align to Key Object", "Align to Key Object", "Align to Key Object", "Align to Key Object", "对齐到关键对象"]),
("Align to Margins", ["Align to Margins", "Align to Margins", "Align to Margins", "Align to Margins", "对齐到边距"]),
("Align to Page", ["Align to Page", "Align to Page", "Align to Page", "Align to Page", "对齐到页面"]),
("Align to Spread", ["Align to Spread", "Align to Spread", "Align to Spread", "Align to Spread", "对齐到跨页"]),
("No Text Wrap", ["No Text Wrap", "No Text Wrap", "No Text Wrap", "No Text Wrap", "无文本绕排"]),
(
"Wrap Around Bounding Box",
["Wrap Around Bounding Box", "Wrap Around Bounding Box", "Wrap Around Bounding Box", "Wrap Around Bounding Box", "绕排定界框"],
),
(
"Wrap Around Object Shape",
["Wrap Around Object Shape", "Wrap Around Object Shape", "Wrap Around Object Shape", "Wrap Around Object Shape", "绕排对象形状"],
),
("Jump Object", ["Jump Object", "Jump Object", "Jump Object", "Jump Object", "跳过对象"]),
("Jump to Next Column", ["Jump to Next Column", "Jump to Next Column", "Jump to Next Column", "Jump to Next Column", "跳至下一栏"]),
("Bulleted List", ["Bulleted List", "Bulleted List", "Bulleted List", "Bulleted List", "项目符号列表"]),
("Numbered List", ["Numbered List", "Numbered List", "Numbered List", "Numbered List", "编号列表"]),
("UPPERCASE", ["UPPERCASE", "UPPERCASE", "UPPERCASE", "UPPERCASE", "大写"]),
("Small Caps", ["Small Caps", "Small Caps", "Small Caps", "Small Caps", "小型大写字母"]),
("Normal", ["Normal", "Normal", "Normal", "Normal", "正常"]),
("Butt", ["Butt", "Butt", "Butt", "Butt", "平头"]),
("Round", ["Round", "Round", "Round", "Round", "圆形"]),
("Projecting", ["Projecting", "Projecting", "Projecting", "Projecting", "突出"]),
("Miter", ["Miter", "Miter", "Miter", "Miter", "斜接"]),
("Bevel", ["Bevel", "Bevel", "Bevel", "Bevel", "斜角"]),
("Center", ["Center", "Center", "Center", "Center", "居中"]),
("Start/End:", ["Start/End:", "Start/End:", "Start/End:", "Start/End:", "起点/终点:"]),
("Left", ["Left", "Left", "Left", "Left", "左"]),
("Right", ["Right", "Right", "Right", "Right", "右"]),
("First", ["First", "First", "First", "First", "首行"]),
("Last", ["Last", "Last", "Last", "Last", "末行"]),
("Before", ["Before", "Before", "Before", "Before", "段前"]),
("After", ["After", "After", "After", "After", "段后"]),
("Shading", ["Shading", "Shading", "Shading", "Shading", "底纹"]),
("Border", ["Border", "Border", "Border", "Border", "边框"]),
("Overprint Fill", ["Overprint Fill", "Overprint Fill", "Overprint Fill", "Overprint Fill", "叠印填色"]),
("Overprint Stroke", ["Overprint Stroke", "Overprint Stroke", "Overprint Stroke", "Overprint Stroke", "叠印描边"]),
("Overprint Gap", ["Overprint Gap", "Overprint Gap", "Overprint Gap", "Overprint Gap", "叠印间隙"]),
("Nonprinting", ["Nonprinting", "Nonprinting", "Nonprinting", "Nonprinting", "不打印"]),
("At Least", ["At Least", "At Least", "At Least", "At Least", "至少"]),
("Exactly", ["Exactly", "Exactly", "Exactly", "Exactly", "固定"]),
("Justify", ["Justify", "Justify", "Justify", "Justify", "两端对齐"]),
("Underline Options", ["Underline Options", "Underline Options", "Underline Options", "Underline Options", "下划线选项"]),
("Strikethrough Options", ["Strikethrough Options", "Strikethrough Options", "Strikethrough Options", "Strikethrough Options", "删除线选项"]),
(
"Discretionary Ligatures",
["Discretionary Ligatures", "Discretionary Ligatures", "Discretionary Ligatures", "Discretionary Ligatures", "任意连字"],
),
("Fractions", ["Fractions", "Fractions", "Fractions", "Fractions", "分数"]),
("Ordinal", ["Ordinal", "Ordinal", "Ordinal", "Ordinal", "序数"]),
("Swash", ["Swash", "Swash", "Swash", "Swash", "花体"]),
("Titling Alternates", ["Titling Alternates", "Titling Alternates", "Titling Alternates", "Titling Alternates", "标题替代字形"]),
("Contextual Alternates", ["Contextual Alternates", "Contextual Alternates", "Contextual Alternates", "Contextual Alternates", "上下文替代字形"]),
("Slashed Zero", ["Slashed Zero", "Slashed Zero", "Slashed Zero", "Slashed Zero", "斜线零"]),
("Tabular Lining", ["Tabular Lining", "Tabular Lining", "Tabular Lining", "Tabular Lining", "等宽齐线数字"]),
("Proportional Oldstyle", ["Proportional Oldstyle", "Proportional Oldstyle", "Proportional Oldstyle", "Proportional Oldstyle", "比例旧式数字"]),
("Proportional Lining", ["Proportional Lining", "Proportional Lining", "Proportional Lining", "Proportional Lining", "比例齐线数字"]),
("Tabular Oldstyle", ["Tabular Oldstyle", "Tabular Oldstyle", "Tabular Oldstyle", "Tabular Oldstyle", "等宽旧式数字"]),
("Default Figure Style", ["Default Figure Style", "Default Figure Style", "Default Figure Style", "Default Figure Style", "默认数字样式"]),
("Not a button", ["Not a button", "Not a button", "Not a button", "Not a button", "非按钮"]),
("Go To Next Page", ["Go To Next Page", "Go To Next Page", "Go To Next Page", "Go To Next Page", "转到下一页"]),
("Go To Previous Page", ["Go To Previous Page", "Go To Previous Page", "Go To Previous Page", "Go To Previous Page", "转到上一页"]),
("Go To First Page", ["Go To First Page", "Go To First Page", "Go To First Page", "Go To First Page", "转到第一页"]),
("Go To Last Page", ["Go To Last Page", "Go To Last Page", "Go To Last Page", "Go To Last Page", "转到最后一页"]),
("Go To Page", ["Go To Page", "Go To Page", "Go To Page", "Go To Page", "转到页面"]),
("Go To URL", ["Go To URL", "Go To URL", "Go To URL", "Go To URL", "转到网址"]),
("Not a form field", ["Not a form field", "Not a form field", "Not a form field", "Not a form field", "非表单域"]),
("Text Field", ["Text Field", "Text Field", "Text Field", "Text Field", "文本域"]),
("Check Box", ["Check Box", "Check Box", "Check Box", "Check Box", "复选框"]),
("Radio Button", ["Radio Button", "Radio Button", "Radio Button", "Radio Button", "单选按钮"]),
("Combo Box", ["Combo Box", "Combo Box", "Combo Box", "Combo Box", "组合框"]),
("List Box", ["List Box", "List Box", "List Box", "List Box", "列表框"]),
("Signature", ["Signature", "Signature", "Signature", "Signature", "签名"]),
("Resize width", ["Resize width", "Resize width", "Resize width", "Resize width", "调整宽度"]),
("Resize height", ["Resize height", "Resize height", "Resize height", "Resize height", "调整高度"]),
("Pin top", ["Pin top", "Pin top", "Pin top", "Pin top", "固定顶部"]),
("Pin bottom", ["Pin bottom", "Pin bottom", "Pin bottom", "Pin bottom", "固定底部"]),
("Pin left", ["Pin left", "Pin left", "Pin left", "Pin left", "固定左侧"]),
("Pin right", ["Pin right", "Pin right", "Pin right", "Pin right", "固定右侧"]),
("Inward", ["Inward", "Inward", "Inward", "Inward", "向内"]),
("Horizontal", ["Horizontal", "Horizontal", "Horizontal", "Horizontal", "水平"]),
("Vertical", ["Vertical", "Vertical", "Vertical", "Vertical", "垂直"]),
("Metrics", ["Metrics", "Metrics", "Metrics", "Metrics", "度量"]),
("Optical", ["Optical", "Optical", "Optical", "Optical", "视觉"]),
("Applies to fill", ["Applies to fill", "Applies to fill", "Applies to fill", "Applies to fill", "应用于填色"]),
("Applies to text", ["Applies to text", "Applies to text", "Applies to text", "Applies to text", "应用于文本"]),
("Gray", ["Gray", "Gray", "Gray", "Gray", "灰度"]),
("Linear", ["Linear", "Linear", "Linear", "Linear", "线性"]),
("Radial", ["Radial", "Radial", "Radial", "Radial", "径向"]),
("Arabic", ["Arabic", "Arabic", "Arabic", "Arabic", "阿拉伯语"]),
("Beveled Rectangle", ["Beveled Rectangle", "Beveled Rectangle", "Beveled Rectangle", "Beveled Rectangle", "斜角矩形"]),
("Chinese", ["Chinese", "Chinese", "Chinese", "Chinese", "中文"]),
(
"Constrain proportions for width and height",
[
"Constrain proportions for width and height",
"Constrain proportions for width and height",
"Constrain proportions for width and height",
"Constrain proportions for width and height",
"约束宽高比例",
],
),
("Corner", ["Corner", "Corner", "Corner", "Corner", "角"]),
("Custom", ["Custom", "Custom", "Custom", "Custom", "自定义"]),
("Czech", ["Czech", "Czech", "Czech", "Czech", "捷克语"]),
("Danish", ["Danish", "Danish", "Danish", "Danish", "丹麦语"]),
("Dashed", ["Dashed", "Dashed", "Dashed", "Dashed", "虚线"]),
("Dotted", ["Dotted", "Dotted", "Dotted", "Dotted", "点线"]),
("Dutch: 2005 Reform", ["Dutch: 2005 Reform", "Dutch: 2005 Reform", "Dutch: 2005 Reform", "Dutch: 2005 Reform", "荷兰语:2005 年拼写改革"]),
("Embedded", ["Embedded", "Embedded", "Embedded", "Embedded", "已嵌入"]),
("English: Canadian", ["English: Canadian", "English: Canadian", "English: Canadian", "English: Canadian", "英语:加拿大"]),
("English: UK", ["English: UK", "English: UK", "English: UK", "English: UK", "英语:英国"]),
("English: USA", ["English: USA", "English: USA", "English: USA", "English: USA", "英语:美国"]),
("Farsi", ["Farsi", "Farsi", "Farsi", "Farsi", "波斯语"]),
("Finnish", ["Finnish", "Finnish", "Finnish", "Finnish", "芬兰语"]),
("Fit Frame to Content", ["Fit Frame to Content", "Fit Frame to Content", "Fit Frame to Content", "Fit Frame to Content", "框架适合内容"]),
("French", ["French", "French", "French", "French", "法语"]),
("French: Canadian", ["French: Canadian", "French: Canadian", "French: Canadian", "French: Canadian", "法语:加拿大"]),
("German: 2006 Reform", ["German: 2006 Reform", "German: 2006 Reform", "German: 2006 Reform", "German: 2006 Reform", "德语:2006 年拼写改革"]),
(
"German: Swiss 2006 Reform",
[
"German: Swiss 2006 Reform",
"German: Swiss 2006 Reform",
"German: Swiss 2006 Reform",
"German: Swiss 2006 Reform",
"德语:瑞士 2006 年拼写改革",
],
),
("Greek", ["Greek", "Greek", "Greek", "Greek", "希腊语"]),
("Hebrew", ["Hebrew", "Hebrew", "Hebrew", "Hebrew", "希伯来语"]),
("Hindi", ["Hindi", "Hindi", "Hindi", "Hindi", "印地语"]),
("Hungarian", ["Hungarian", "Hungarian", "Hungarian", "Hungarian", "匈牙利语"]),
(
"Inverse Rounded Rectangle",
["Inverse Rounded Rectangle", "Inverse Rounded Rectangle", "Inverse Rounded Rectangle", "Inverse Rounded Rectangle", "内凹圆角矩形"],
),
("Italian", ["Italian", "Italian", "Italian", "Italian", "意大利语"]),
("Japanese", ["Japanese", "Japanese", "Japanese", "Japanese", "日语"]),
("Korean", ["Korean", "Korean", "Korean", "Korean", "韩语"]),
(
"Make all settings the same",
["Make all settings the same", "Make all settings the same", "Make all settings the same", "Make all settings the same", "统一所有设置"],
),
("Native", ["Native", "Native", "Native", "Native", "本地数字"]),
("Norwegian: Bokmål", ["Norwegian: Bokmål", "Norwegian: Bokmål", "Norwegian: Bokmål", "Norwegian: Bokmål", "挪威语:书面语"]),
("Orthogonal Line", ["Orthogonal Line", "Orthogonal Line", "Orthogonal Line", "Orthogonal Line", "正交线"]),
("Polish", ["Polish", "Polish", "Polish", "Polish", "波兰语"]),
("Portuguese", ["Portuguese", "Portuguese", "Portuguese", "Portuguese", "葡萄牙语"]),
("Portuguese: Brazilian", ["Portuguese: Brazilian", "Portuguese: Brazilian", "Portuguese: Brazilian", "Portuguese: Brazilian", "葡萄牙语:巴西"]),
("Rounded Rectangle", ["Rounded Rectangle", "Rounded Rectangle", "Rounded Rectangle", "Rounded Rectangle", "圆角矩形"]),
("Russian", ["Russian", "Russian", "Russian", "Russian", "俄语"]),
("Solid", ["Solid", "Solid", "Solid", "Solid", "实线"]),
("Spanish", ["Spanish", "Spanish", "Spanish", "Spanish", "西班牙语"]),
("Story Editor", ["Story Editor", "Story Editor", "Story Editor", "Story Editor", "文章编辑器"]),
("Swedish", ["Swedish", "Swedish", "Swedish", "Swedish", "瑞典语"]),
("Thick - Thick", ["Thick - Thick", "Thick - Thick", "Thick - Thick", "Thick - Thick", "粗线-粗线"]),
("Thick - Thin", ["Thick - Thin", "Thick - Thin", "Thick - Thin", "Thick - Thin", "粗线-细线"]),
("Thin - Thick", ["Thin - Thick", "Thin - Thick", "Thin - Thick", "Thin - Thick", "细线-粗线"]),
("Thin - Thin", ["Thin - Thin", "Thin - Thin", "Thin - Thin", "Thin - Thin", "细线-细线"]),
("Thin - Thick - Thin", ["Thin - Thick - Thin", "Thin - Thick - Thin", "Thin - Thick - Thin", "Thin - Thick - Thin", "细线-粗线-细线"]),
("Thick - Thin - Thick", ["Thick - Thin - Thick", "Thick - Thin - Thick", "Thick - Thin - Thick", "Thick - Thin - Thick", "粗线-细线-粗线"]),
("Wavy", ["Wavy", "Wavy", "Wavy", "Wavy", "波浪线"]),
("Straight Hash", ["Straight Hash", "Straight Hash", "Straight Hash", "Straight Hash", "直斜线"]),
("Stripes", ["Stripes", "Stripes", "Stripes", "Stripes", "条纹"]),
("Triangle", ["Triangle", "Triangle", "Triangle", "Triangle", "三角形"]),
("Turkish", ["Turkish", "Turkish", "Turkish", "Turkish", "土耳其语"]),
("Ukrainian", ["Ukrainian", "Ukrainian", "Ukrainian", "Ukrainian", "乌克兰语"]),
("Cell Style", ["Cell Style", "Cell Style", "Cell Style", "Cell Style", "单元格样式"]),
("Table Style", ["Table Style", "Table Style", "Table Style", "Table Style", "表格样式"]),
("Col ←", ["Col ←", "Col ←", "Col ←", "Col ←", "左侧插入列"]),
("Col →", ["Col →", "Col →", "Col →", "Col →", "右侧插入列"]),
("Row ↑", ["Row ↑", "Row ↑", "Row ↑", "Row ↑", "上方插入行"]),
("Row ↓", ["Row ↓", "Row ↓", "Row ↓", "Row ↓", "下方插入行"]),
("Hide Others", ["Hide Others", "Hide Others", "Hide Others", "Hide Others", "隐藏其他图层"]),
("Lock Others", ["Lock Others", "Lock Others", "Lock Others", "Lock Others", "锁定其他图层"]),
("Show All Layers", ["Show All Layers", "Show All Layers", "Show All Layers", "Show All Layers", "显示所有图层"]),
("Unlock", ["Unlock", "Unlock", "Unlock", "Unlock", "解锁"]),
("Unlock All Layers", ["Unlock All Layers", "Unlock All Layers", "Unlock All Layers", "Unlock All Layers", "解锁所有图层"]),
("Added", ["Added", "Added", "Added", "Added", "已添加"]),
("Deleted", ["Deleted", "Deleted", "Deleted", "Deleted", "已删除"]),
("Guide-based", ["Guide-based", "Guide-based", "Guide-based", "Guide-based", "基于参考线"]),
("Loop", ["Loop", "Loop", "Loop", "Loop", "循环播放"]),
("Object-based", ["Object-based", "Object-based", "Object-based", "Object-based", "基于对象"]),
("Off", ["Off", "Off", "Off", "Off", "关闭"]),
("Play on Page Load", ["Play on Page Load", "Play on Page Load", "Play on Page Load", "Play on Page Load", "页面载入时播放"]),
("Re-center", ["Re-center", "Re-center", "Re-center", "Re-center", "重新居中"]),
("Show Controls", ["Show Controls", "Show Controls", "Show Controls", "Show Controls", "显示控件"]),
("Signature Field", ["Signature Field", "Signature Field", "Signature Field", "Signature Field", "签名域"]),
(
"Select text to apply a condition",
[
"Select text to apply a condition",
"Select text to apply a condition",
"Select text to apply a condition",
"Select text to apply a condition",
"选择文本以应用条件",
],
),
("Blinds", ["Blinds", "Blinds", "Blinds", "Blinds", "百叶窗"]),
("Box", ["Box", "Box", "Box", "Box", "方框"]),
("Comb", ["Comb", "Comb", "Comb", "Comb", "梳状"]),
("Cover", ["Cover", "Cover", "Cover", "Cover", "覆盖"]),
("Dissolve", ["Dissolve", "Dissolve", "Dissolve", "Dissolve", "溶解"]),
("Fade", ["Fade", "Fade", "Fade", "Fade", "淡入淡出"]),
("Push", ["Push", "Push", "Push", "Push", "推入"]),
("Split", ["Split", "Split", "Split", "Split", "分割"]),
("Uncover", ["Uncover", "Uncover", "Uncover", "Uncover", "揭开"]),
("Wipe", ["Wipe", "Wipe", "Wipe", "Wipe", "擦除"]),
("Simple", ["Simple", "Simple", "Simple", "Simple", "简单"]),
("Simple - Wide", ["Simple - Wide", "Simple - Wide", "Simple - Wide", "Simple - Wide", "简单-宽"]),
("Triangle - Wide", ["Triangle - Wide", "Triangle - Wide", "Triangle - Wide", "Triangle - Wide", "三角形-宽"]),
("Barbed", ["Barbed", "Barbed", "Barbed", "Barbed", "倒钩"]),
("Curved", ["Curved", "Curved", "Curved", "Curved", "曲线"]),
("Circle", ["Circle", "Circle", "Circle", "Circle", "圆形"]),
("Circle - Solid", ["Circle - Solid", "Circle - Solid", "Circle - Solid", "Circle - Solid", "实心圆"]),
("Square", ["Square", "Square", "Square", "Square", "正方形"]),
("Square - Solid", ["Square - Solid", "Square - Solid", "Square - Solid", "Square - Solid", "实心正方形"]),
("Bar", ["Bar", "Bar", "Bar", "Bar", "短线"]),
("Fit Text", ["Fit Text", "Fit Text", "Fit Text", "Fit Text", "适合文本"]),
("Preview (W)", ["Preview (W)", "Preview (W)", "Preview (W)", "Preview (W)", "预览 (W)"]),
("Normal (W)", ["Normal (W)", "Normal (W)", "Normal (W)", "Normal (W)", "正常 (W)"]),
("objects", ["objects", "objects", "objects", "objects", "个对象"]),
("pages", ["pages", "pages", "pages", "pages", "页"]),
("stories", ["stories", "stories", "stories", "stories", "篇文章"]),
("items", ["items", "items", "items", "items", "个项目"]),
("warnings", ["warnings", "warnings", "warnings", "warnings", "个警告"]),
("Render", ["Render", "Render", "Render", "Render", "渲染"]),
("Set", ["Set", "Set", "Set", "Set", "样式集"]),
("unused", ["unused", "unused", "unused", "unused", "未使用"]),
("⚠ Missing", ["⚠ Missing", "⚠ Missing", "⚠ Missing", "⚠ Missing", "⚠ 缺失"]),
("▲ Modified", ["▲ Modified", "▲ Modified", "▲ Modified", "▲ Modified", "▲ 已修改"]),
("(Text Color)", ["(Text Color)", "(Text Color)", "(Text Color)", "(Text Color)", "(文本颜色)"]),
(
"{count} glyphs — click to insert at the text cursor",
[
"{count} glyphs — click to insert at the text cursor",
"{count} glyphs — click to insert at the text cursor",
"{count} glyphs — click to insert at the text cursor",
"{count} glyphs — click to insert at the text cursor",
"{count} 个字形 — 单击插入到文本光标处",
],
),
(
"{pages} Pages in {spreads} Spreads",
[
"{pages} Pages in {spreads} Spreads",
"{pages} Pages in {spreads} Spreads",
"{pages} Pages in {spreads} Spreads",
"{pages} Pages in {spreads} Spreads",
"共 {pages} 页,{spreads} 个跨页",
],
),
(" (auto)", [" (auto)", " (auto)", " (auto)", " (auto)", "(自动)"]),
(
"Absolute Numbering (instead of Section Numbering)",
[
"Absolute Numbering (instead of Section Numbering)",
"Absolute Numbering (instead of Section Numbering)",
"Absolute Numbering (instead of Section Numbering)",
"Absolute Numbering (instead of Section Numbering)",
"绝对页码(而非章节页码)",
],
),
(
"Add and remove pages as the primary text frame's story grows and shrinks",
[
"Add and remove pages as the primary text frame's story grows and shrinks",
"Add and remove pages as the primary text frame's story grows and shrinks",
"Add and remove pages as the primary text frame's story grows and shrinks",
"Add and remove pages as the primary text frame's story grows and shrinks",
"随主文本框架内容增减自动添加或删除页面",
],
),
(
"Added Text (highlighted)",
["Added Text (highlighted)", "Added Text (highlighted)", "Added Text (highlighted)", "Added Text (highlighted)", "新增文本(高亮)"],
),
(
"Adjust Layout (objects follow the new page size)",
[
"Adjust Layout (objects follow the new page size)",
"Adjust Layout (objects follow the new page size)",
"Adjust Layout (objects follow the new page size)",
"Adjust Layout (objects follow the new page size)",
"调整版面(对象随页面尺寸调整)",
],
),
("Auto-Fit", ["Auto-Fit", "Auto-Fit", "Auto-Fit", "Auto-Fit", "自动适合"]),
("Case sensitive", ["Case sensitive", "Case sensitive", "Case sensitive", "Case sensitive", "区分大小写"]),
(
"Custom Tracking/Kerning",
["Custom Tracking/Kerning", "Custom Tracking/Kerning", "Custom Tracking/Kerning", "Custom Tracking/Kerning", "自定义字距/字偶距"],
),
(
"Dimensions Include Stroke Weight",
[
"Dimensions Include Stroke Weight",
"Dimensions Include Stroke Weight",
"Dimensions Include Stroke Weight",
"Dimensions Include Stroke Weight",
"尺寸包含描边粗细",
],
),
("Enable Autocorrect", ["Enable Autocorrect", "Enable Autocorrect", "Enable Autocorrect", "Enable Autocorrect", "启用自动更正"]),
(
"Enable Dynamic Spelling (underline misspelled words)",
[
"Enable Dynamic Spelling (underline misspelled words)",
"Enable Dynamic Spelling (underline misspelled words)",
"Enable Dynamic Spelling (underline misspelled words)",
"Enable Dynamic Spelling (underline misspelled words)",
"启用动态拼写检查(在拼写错误下加线)",
],
),
("Grids in Back", ["Grids in Back", "Grids in Back", "Grids in Back", "Grids in Back", "网格置于后方"]),
("H&J Violations", ["H&J Violations", "H&J Violations", "H&J Violations", "H&J Violations", "断字与对齐违规"]),
("Include Bleed", ["Include Bleed", "Include Bleed", "Include Bleed", "Include Bleed", "包含出血"]),
("Include Stroke Weight", ["Include Stroke Weight", "Include Stroke Weight", "Include Stroke Weight", "Include Stroke Weight", "包含描边粗细"]),
("Join our Discord", ["Join our Discord", "Join our Discord", "Join our Discord", "Join our Discord", "加入我们的 Discord 社区"]),
("Keep Violations", ["Keep Violations", "Keep Violations", "Keep Violations", "Keep Violations", "保持选项违规"]),
(
"Overprint [Black] Swatch at 100%",
[
"Overprint [Black] Swatch at 100%",
"Overprint [Black] Swatch at 100%",
"Overprint [Black] Swatch at 100%",
"Overprint [Black] Swatch at 100%",
"100% 黑色色板叠印",
],
),
("Printer's Marks", ["Printer's Marks", "Printer's Marks", "Printer's Marks", "Printer's Marks", "打印标记"]),
(
"Remove Styles and Formatting from Text and Tables",
[
"Remove Styles and Formatting from Text and Tables",
"Remove Styles and Formatting from Text and Tables",
"Remove Styles and Formatting from Text and Tables",
"Remove Styles and Formatting from Text and Tables",
"移除文本和表格中的样式与格式",
],
),
("Ruby", ["Ruby", "Ruby", "Ruby", "Ruby", "注音"]),
(
"Show Note Anchors in Layout View",
[
"Show Note Anchors in Layout View",
"Show Note Anchors in Layout View",
"Show Note Anchors in Layout View",
"Show Note Anchors in Layout View",
"在版面视图显示附注锚点",
],
),
("Spreads", ["Spreads", "Spreads", "Spreads", "Spreads", "跨页"]),
("Substituted Fonts", ["Substituted Fonts", "Substituted Fonts", "Substituted Fonts", "Substituted Fonts", "替换的字体"]),
(
"Transformations are Totals",
["Transformations are Totals", "Transformations are Totals", "Transformations are Totals", "Transformations are Totals", "显示累计变换值"],
),
(
"Use Black Point Compensation",
[
"Use Black Point Compensation",
"Use Black Point Compensation",
"Use Black Point Compensation",
"Use Black Point Compensation",
"使用黑场补偿",
],
),
(
"Use Typographer's Quotes",
["Use Typographer's Quotes", "Use Typographer's Quotes", "Use Typographer's Quotes", "Use Typographer's Quotes", "使用排版引号"],
),
("Whole word", ["Whole word", "Whole word", "Whole word", "Whole word", "全字匹配"]),
("Interface", ["Interface", "Interface", "Interface", "Interface", "界面"]),
("Advanced Type", ["Advanced Type", "Advanced Type", "Advanced Type", "Advanced Type", "高级文字"]),
("Composition", ["Composition", "Composition", "Composition", "Composition", "排版"]),
("Units & Increments", ["Units & Increments", "Units & Increments", "Units & Increments", "Units & Increments", "单位与增量"]),
("Grids", ["Grids", "Grids", "Grids", "Grids", "网格"]),
("Guides & Pasteboard", ["Guides & Pasteboard", "Guides & Pasteboard", "Guides & Pasteboard", "Guides & Pasteboard", "参考线与粘贴板"]),
("Dictionary", ["Dictionary", "Dictionary", "Dictionary", "Dictionary", "词典"]),
("Autocorrect", ["Autocorrect", "Autocorrect", "Autocorrect", "Autocorrect", "自动更正"]),
("Story Editor Display", ["Story Editor Display", "Story Editor Display", "Story Editor Display", "Story Editor Display", "文章编辑器显示"]),
("File Handling", ["File Handling", "File Handling", "File Handling", "File Handling", "文件处理"]),
(
"Formatting affects container (J)",
[
"Formatting affects container (J)",
"Formatting affects container (J)",
"Formatting affects container (J)",
"Formatting affects container (J)",
"格式应用于容器 (J)",
],
),
(
"Formatting affects text (J)",
[
"Formatting affects text (J)",
"Formatting affects text (J)",
"Formatting affects text (J)",
"Formatting affects text (J)",
"格式应用于文字 (J)",
],
),
("Apply None", ["Apply None", "Apply None", "Apply None", "Apply None", "应用无色"]),
("Height Only", ["Height Only", "Height Only", "Height Only", "Height Only", "仅高度"]),
("Width Only", ["Width Only", "Width Only", "Width Only", "Width Only", "仅宽度"]),
("Height and Width", ["Height and Width", "Height and Width", "Height and Width", "Height and Width", "高度和宽度"]),
("Height only", ["Height only", "Height only", "Height only", "Height only", "仅高度"]),
("Width only", ["Width only", "Width only", "Width only", "Width only", "仅宽度"]),
("Height and width", ["Height and width", "Height and width", "Height and width", "Height and width", "高度和宽度"]),
("Version", ["Version", "Version", "Version", "Version", "版本"]),
(
"{count} characters don't fit",
[
"{count} characters don't fit",
"{count} characters don't fit",
"{count} characters don't fit",
"{count} characters don't fit",
"有 {count} 个字符溢出",
],
),
("document", ["document", "document", "document", "document", "文档"]),
("story", ["story", "story", "story", "story", "文章"]),
("selection", ["selection", "selection", "selection", "selection", "选区"]),
("top", ["top", "top", "top", "top", "顶部"]),
("center", ["center", "center", "center", "center", "居中"]),
("bottom", ["bottom", "bottom", "bottom", "bottom", "底部"]),
("justify", ["justify", "justify", "justify", "justify", "两端对齐"]),
("text", ["text", "text", "text", "text", "文字"]),
("graphic", ["graphic", "graphic", "graphic", "graphic", "图形"]),
("unassigned", ["unassigned", "unassigned", "unassigned", "unassigned", "未指定"]),
("Perceptual", ["Perceptual", "Perceptual", "Perceptual", "Perceptual", "可感知"]),
("Relative Colorimetric", ["Relative Colorimetric", "Relative Colorimetric", "Relative Colorimetric", "Relative Colorimetric", "相对比色"]),
("Saturation", ["Saturation", "Saturation", "Saturation", "Saturation", "饱和度"]),
("Absolute Colorimetric", ["Absolute Colorimetric", "Absolute Colorimetric", "Absolute Colorimetric", "Absolute Colorimetric", "绝对比色"]),
("No conflicts", ["No conflicts", "No conflicts", "No conflicts", "No conflicts", "没有冲突"]),
("Page (1–{count}):", ["Page (1–{count}):", "Page (1–{count}):", "Page (1–{count}):", "Page (1–{count}):", "页码 (1–{count}):"]),
(
"Fonts in Document: {count} Missing: {missing}",
[
"Fonts in Document: {count} Missing: {missing}",
"Fonts in Document: {count} Missing: {missing}",
"Fonts in Document: {count} Missing: {missing}",
"Fonts in Document: {count} Missing: {missing}",
"文档中的字体:{count} 缺失:{missing}",
],
),
];
/// Interface direction; independent of document binding and paragraph direction.
@@ -1816,6 +2626,22 @@ pub fn tr<'a>(lang: &str, s: &'a str) -> &'a str {
}
}
/// Localize reserved built-in style names only; user-defined names are document data.
pub fn style_name<'a>(lang: &str, name: &'a str) -> &'a str {
match name {
designcraft_doc::NO_PARA_STYLE | designcraft_doc::BASIC_PARAGRAPH | designcraft_doc::NO_CHAR_STYLE => tr(lang, name),
_ => name,
}
}
/// Built-in workspace captions; custom workspace names remain unchanged.
pub fn workspace_name<'a>(lang: &str, name: &'a str) -> &'a str {
match name {
"Essentials" | "Advanced" | "Book" | "Digital Publishing" | "Interactive for PDF" | "Printing and Proofing" | "Typography" => tr(lang, name),
_ => name,
}
}
#[cfg(test)]
mod tests {
use super::*;
+736
View File
@@ -273,4 +273,740 @@ pub(super) const TABLE: &[(&str, &str)] = &[
("Options", "خيارات"),
("More Options", "خيارات إضافية"),
("Dialog", "مربع حوار"),
("General", "عام"),
("Basic Character Formats", "تنسيقات الأحرف الأساسية"),
("Indents and Spacing", "المسافات البادئة والتباعد"),
("Justification", "الضبط"),
("Drop Caps and Nested Styles", "الأحرف الاستهلالية والأنماط المتداخلة"),
("GREP Style", "نمط GREP"),
("Character Color", "لون الأحرف"),
("Export Tagging", "وسوم التصدير"),
("Numbering and Formatting", "الترقيم والتنسيق"),
("Spacing Options", "خيارات التباعد"),
("First Baseline", "خط الأساس الأول"),
("Rule Above", "خط علوي"),
("Numbering", "الترقيم"),
("Footnote Reference Number in Text", "رقم مرجع الحاشية في النص"),
("Footnote Formatting", "تنسيق الحواشي"),
("Span Footnotes Across Columns", "امتداد الحواشي عبر الأعمدة"),
("Rule On", "إظهار الخط"),
("Ascent", "ارتفاع الحروف الصاعدة"),
("Cap Height", "ارتفاع الأحرف الكبيرة"),
("x Height", "ارتفاع حرف x"),
("Fixed", "ثابت"),
("Never (continuous)", "أبدًا (ترقيم متصل)"),
("Spread", "صفحتان متقابلتان"),
("Section", "قسم"),
("Footnote Reference", "مرجع الحاشية"),
("Footnote Text", "نص الحاشية"),
("Both", "كلاهما"),
("Apply Superscript", "تطبيق مرتفع"),
("Apply Subscript", "تطبيق منخفض"),
("Apply Normal", "تطبيق عادي"),
("OpenType Superscript", "مرتفع OpenType"),
("through", "حتى شاملًا"),
("up to", "حتى باستثناء"),
("line", "سطر"),
("lines", "أسطر"),
("sentences", "جمل"),
("words", "كلمات"),
("characters", "محارف"),
("letters", "حروف"),
("digits", "أرقام"),
("tab", "جدولة"),
("forcedLineBreak", "فاصل سطر إجباري"),
("emSpace", "مسافة em"),
("enSpace", "مسافة en"),
("chars", "محارف محددة"),
("[Automatic]", "[تلقائي]"),
("Size:", "الحجم:"),
("Tracking:", "التباعد:"),
("Left Indent:", "المسافة البادئة اليسرى:"),
("Right Indent:", "المسافة البادئة اليمنى:"),
("First Line Indent:", "المسافة البادئة للسطر الأول:"),
("Space Before:", "المسافة قبل الفقرة:"),
("Space After:", "المسافة بعد الفقرة:"),
("Words with at Least:", "الحد الأدنى لطول الكلمة:"),
("After First:", "الأحرف قبل الواصلة:"),
("Before Last:", "الأحرف بعد الواصلة:"),
("Hyphen Limit:", "حد الأسطر المتتالية بواصلة:"),
("Word Spacing:", "تباعد الكلمات:"),
("Letter Spacing:", "تباعد الحروف:"),
("Glyph Scaling:", "تحجيم الحروف:"),
("Insert Kashidas:", "إدراج الكشيدة:"),
("In justified Arabic text", "في النص العربي المضبوط"),
("Minimum Space Before First Footnote:", "أقل مسافة قبل الحاشية الأولى:"),
("Space Between Footnotes:", "المسافة بين الحواشي:"),
("Offset:", "الإزاحة:"),
("Min:", "الحد الأدنى:"),
("Weight:", "السماكة:"),
("Color:", "اللون:"),
("Width:", "العرض:"),
("Style:", "النمط:"),
("Start at:", "بدء الترقيم من:"),
("Restart Numbering Every:", "إعادة بدء الترقيم كل:"),
("Show Prefix/Suffix in:", "إظهار البادئة واللاحقة في:"),
("Prefix:", "البادئة:"),
("Suffix:", "اللاحقة:"),
("Position:", "الموضع:"),
("Character Style:", "نمط الأحرف:"),
("Paragraph Style:", "نمط الفقرة:"),
("Separator:", "الفاصل:"),
("EPUB and HTML", "EPUB وHTML"),
("Tag:", "الوسم:"),
("Class:", "الفئة:"),
("Nested Styles", "الأنماط المتداخلة"),
("Nested Line Styles", "أنماط الأسطر المتداخلة"),
("New Nested Style", "نمط متداخل جديد"),
("New Line Style", "نمط سطر جديد"),
("GREP Styles", "أنماط GREP"),
("Apply Style:", "تطبيق النمط:"),
("To Text:", "على النص:"),
("New GREP Style", "نمط GREP جديد"),
("Font Family:", "عائلة الخط:"),
("Font Style:", "نمط الخط:"),
("Leading:", "تباعد الأسطر:"),
("Alignment:", "المحاذاة:"),
("Hyphenate", "إضافة واصلات"),
("Minimum", "الحد الأدنى"),
("Desired", "المطلوب"),
("Maximum", "الحد الأقصى"),
("Composer:", "منضّد النص:"),
("Paragraph Composer", "منضّد الفقرات"),
("Single-line Composer", "منضّد السطر الواحد"),
("Style Name:", "اسم النمط:"),
("Based On:", "استنادًا إلى:"),
("for", "لعدد"),
("Hyphenation Exceptions", "استثناءات الواصلة"),
("Binding:", "اتجاه التجليد:"),
("Dynamic Spelling", "التدقيق الإملائي التلقائي"),
("UI Scaling", "تحجيم الواجهة"),
("Ruby:", "روبي:"),
("Set over the selected text; empty removes it.", "تُضاف فوق النص المحدد؛ اتركها فارغة للإزالة."),
("Document Footnote Options…", "خيارات حواشي المستند…"),
("Insert Footnote", "إدراج حاشية"),
("Go to Footnote Reference", "الانتقال إلى مرجع الحاشية"),
("Footnotes", "الحواشي"),
("Paragraph Style Options…", "خيارات نمط الفقرة…"),
("New Paragraph Style…", "نمط فقرة جديد…"),
("New Character Style…", "نمط أحرف جديد…"),
("[Basic Paragraph]", "[فقرة أساسية]"),
("[No Paragraph Style]", "[بلا نمط فقرة]"),
("Align left", "محاذاة إلى اليسار"),
("Align center", "محاذاة إلى الوسط"),
("Align right", "محاذاة إلى اليمين"),
("Justify with last line aligned left", "ضبط مع محاذاة السطر الأخير إلى اليسار"),
("Justify with last line aligned center", "ضبط مع توسيط السطر الأخير"),
("Justify with last line aligned right", "ضبط مع محاذاة السطر الأخير إلى اليمين"),
("Justify all lines", "ضبط جميع الأسطر"),
("Align towards spine", "محاذاة نحو الكعب"),
("Align away from spine", "محاذاة بعيدًا عن الكعب"),
("Create New Style from Selection", "إنشاء نمط جديد من التحديد"),
("No text selected", "لم يتم تحديد نص"),
("Essentials", "الأساسيات"),
("Advanced", "متقدم"),
("Book", "كتاب"),
("Digital Publishing", "النشر الرقمي"),
("Interactive for PDF", "التفاعل في PDF"),
("Printing and Proofing", "الطباعة والتدقيق"),
("Typography", "تنضيد النصوص"),
("No errors", "لا توجد أخطاء"),
("error", "خطأ"),
("errors", "أخطاء"),
("Display All Blacks as Rich Black", "عرض كل درجات الأسود كأسود غني"),
("Display All Blacks Accurately", "عرض كل درجات الأسود بدقة"),
("Output All Blacks as Rich Black", "إخراج كل درجات الأسود كأسود غني"),
("Output All Blacks Accurately", "إخراج كل درجات الأسود بدقة"),
("Top Margin", "الهامش العلوي"),
("Top of Page", "أعلى الصفحة"),
("Paragraph Styles panel", "لوحة أنماط الفقرات"),
("Character Styles panel", "لوحة أنماط الأحرف"),
("Create new style", "إنشاء نمط جديد"),
("Clear overrides", "مسح التجاوزات"),
("Accept", "قبول"),
("Reject", "رفض"),
("Add Selection", "إضافة التحديد"),
("Add the selection", "إضافة التحديد"),
("Add to Swatches", "إضافة إلى الحوامل"),
("Align Stroke:", "محاذاة الحد:"),
("All Caps", "أحرف كبيرة"),
("Angle", "الزاوية"),
("Angle:", "الزاوية:"),
("Baseline", "خط الأساس"),
("Cap:", "نهاية الخط:"),
("Cell Text", "نص الخلية"),
("Cell Type", "نوع الخلية"),
("Column Width", "عرض العمود"),
("Composer", "منضّد النص"),
("Convert Selection to Multi-State Object", "تحويل التحديد إلى كائن متعدد الحالات"),
("Convert to Text", "تحويل إلى نص"),
("Create New Layer", "إنشاء طبقة جديدة"),
("Default", "افتراضي"),
("Delete DTD", "حذف DTD"),
("Delete Layer", "حذف الطبقة"),
("Delete Page", "حذف الصفحة"),
("Digits", "الأرقام"),
("Distance", "المسافة"),
("Distribute ↔", "توزيع أفقي"),
("Distribute ↕", "توزيع عمودي"),
("Drop Shadow", "ظل ساقط"),
("Effects: Drop Shadow", "التأثيرات: ظل ساقط"),
("Favorites", "المفضلة"),
("First Spread", "أول صفحتين متقابلتين"),
("Frame Fitting", "ملاءمة الإطار"),
("Gap Color:", "لون الفجوة:"),
("Go To", "انتقال إلى"),
("Graphic…", "رسم…"),
("Home", "الرئيسية"),
("Hyperlink from the selected text or frames", "إنشاء رابط من النص أو الإطارات المحددة"),
("Include when exporting", "تضمين عند التصدير"),
("Join:", "وصلة الخط:"),
("Last Spread", "آخر صفحتين متقابلتين"),
("Linked Stories", "النصوص المرتبطة"),
("Load DTD…", "تحميل DTD…"),
("Location:", "الموضع:"),
("Miter Limit:", "حد الوصلة الحادة:"),
("Move", "نقل"),
("New Article from Selection", "مقال جديد من التحديد"),
("New Note at Cursor", "ملاحظة جديدة عند المؤشر"),
("New condition name", "اسم الشرط الجديد"),
("New group…", "مجموعة جديدة…"),
("New tag", "وسم جديد"),
("Next Spread", "الصفحتان التاليتان"),
("Note text", "نص الملاحظة"),
("Offset", "الإزاحة"),
("Offsets", "الإزاحات"),
("Open", "فتح"),
("Paragraph alignment", "محاذاة الفقرة"),
("Pick a file in the folder holding the moved graphics", "اختر ملفًا من المجلد الذي يحتوي على الرسومات المنقولة"),
("Place", "إدراج"),
("Previous Spread", "الصفحتان السابقتان"),
("Release to Objects", "تحرير إلى كائنات"),
("Reverse the gradient", "عكس التدرج"),
("Rotate 90° Counterclockwise", "تدوير 90° عكس عقارب الساعة"),
("Rotation Angle", "زاوية الدوران"),
("Row Height", "ارتفاع الصف"),
("Run", "تشغيل"),
("Scale X Percentage", "نسبة التحجيم الأفقي"),
("Scale Y Percentage", "نسبة التحجيم العمودي"),
("Search commands", "البحث عن الأوامر"),
("Search fonts", "البحث عن الخطوط"),
("Search styles and commands…", "البحث عن الأنماط والأوامر…"),
("Search: character or U+code", "البحث بحرف أو رمز U+"),
("Separated by commas", "مفصولة بفواصل"),
("Share", "مشاركة"),
("Shear X Angle", "زاوية الميل الأفقي"),
("Show / hide text with this condition", "إظهار أو إخفاء النص بهذا الشرط"),
("Show Favorites Only", "إظهار المفضلة فقط"),
("Show in the text", "إظهار في النص"),
("Spreads of up to 10 pages", "مجموعات تصل إلى 10 صفحات"),
("Stop Color:", "لون نقطة التدرج:"),
("Strikethrough", "يتوسطه خط"),
("Structure", "البنية"),
("Style", "النمط"),
("Style source", "مصدر الأنماط"),
("Styles panel", "لوحة الأنماط"),
("Superscript", "مرتفع"),
("Tag the selection", "إضافة وسم للتحديد"),
("The structure is valid.", "البنية صالحة."),
("Toggle double column", "تبديل التخطيط بعمودين"),
("Type:", "النوع:"),
("Underline", "تسطير"),
("Untag Selection", "إزالة الوسم عن التحديد"),
("Update", "تحديث"),
("Validate", "تحقق"),
("Weight", "السماكة"),
("Subtract", "طرح"),
("Intersect", "تقاطع"),
("Exclude Overlap", "استبعاد التداخل"),
("Minus Back", "طرح الخلفية"),
("Make Compound", "إنشاء مسار مركب"),
("Release", "تحرير"),
("Paths", "مسارات"),
("Align left edges", "محاذاة الحواف اليسرى"),
("Align horizontal centers", "محاذاة المراكز أفقيًا"),
("Align right edges", "محاذاة الحواف اليمنى"),
("Align top edges", "محاذاة الحواف العليا"),
("Align vertical centers", "محاذاة المراكز عموديًا"),
("Align bottom edges", "محاذاة الحواف السفلى"),
("Align to Selection", "محاذاة إلى التحديد"),
("Align to Key Object", "محاذاة إلى الكائن الرئيسي"),
("Align to Margins", "محاذاة إلى الهوامش"),
("Align to Page", "محاذاة إلى الصفحة"),
("Align to Spread", "محاذاة إلى الصفحات المتقابلة"),
("No Text Wrap", "بلا التفاف للنص"),
("Wrap Around Bounding Box", "التفاف حول المربع المحيط"),
("Wrap Around Object Shape", "التفاف حول شكل الكائن"),
("Jump Object", "تجاوز الكائن"),
("Jump to Next Column", "انتقال إلى العمود التالي"),
("Bulleted List", "قائمة نقطية"),
("Numbered List", "قائمة مرقمة"),
("UPPERCASE", "أحرف كبيرة"),
("Small Caps", "أحرف كبيرة صغيرة الحجم"),
("Butt", "مسطحة"),
("Round", "مستديرة"),
("Projecting", "بارزة"),
("Miter", "حادة"),
("Bevel", "مشطوفة"),
("Center", "وسط"),
("Start/End:", "البداية/النهاية:"),
("First", "الأول"),
("Last", "الأخير"),
("Before", "قبل"),
("After", "بعد"),
("Shading", "تظليل"),
("Border", "إطار"),
("Overprint Fill", "طباعة التعبئة فوقيًا"),
("Overprint Stroke", "طباعة الحد فوقيًا"),
("Overprint Gap", "طباعة الفجوة فوقيًا"),
("Nonprinting", "غير قابل للطباعة"),
("At Least", "على الأقل"),
("Exactly", "بالضبط"),
("Justify", "ضبط"),
("Underline Options", "خيارات التسطير"),
("Strikethrough Options", "خيارات الخط المتوسط"),
("Discretionary Ligatures", "روابط اختيارية"),
("Fractions", "كسور"),
("Ordinal", "ترتيبي"),
("Swash", "زخرفي"),
("Titling Alternates", "بدائل العناوين"),
("Contextual Alternates", "بدائل سياقية"),
("Slashed Zero", "صفر مشطوب"),
("Tabular Lining", "أرقام منتظمة ثابتة العرض"),
("Proportional Oldstyle", "أرقام قديمة متناسبة"),
("Proportional Lining", "أرقام منتظمة متناسبة"),
("Tabular Oldstyle", "أرقام قديمة ثابتة العرض"),
("Default Figure Style", "نمط الأرقام الافتراضي"),
("Not a button", "ليس زرًا"),
("Go To Next Page", "انتقال إلى الصفحة التالية"),
("Go To Previous Page", "انتقال إلى الصفحة السابقة"),
("Go To First Page", "انتقال إلى الصفحة الأولى"),
("Go To Last Page", "انتقال إلى الصفحة الأخيرة"),
("Go To Page", "انتقال إلى صفحة"),
("Go To URL", "انتقال إلى عنوان URL"),
("Not a form field", "ليس حقل نموذج"),
("Text Field", "حقل نص"),
("Check Box", "خانة اختيار"),
("Radio Button", "زر اختيار"),
("Combo Box", "مربع تحرير وسرد"),
("List Box", "مربع قائمة"),
("Signature", "توقيع"),
("Resize width", "تغيير العرض"),
("Resize height", "تغيير الارتفاع"),
("Pin top", "تثبيت الأعلى"),
("Pin bottom", "تثبيت الأسفل"),
("Pin left", "تثبيت اليسار"),
("Pin right", "تثبيت اليمين"),
("Inward", "إلى الداخل"),
("Metrics", "مقاييس"),
("Optical", "بصري"),
("Applies to fill", "يُطبّق على التعبئة"),
("Applies to text", "يُطبّق على النص"),
("Gray", "رمادي"),
("Linear", "خطي"),
("Radial", "شعاعي"),
("Arabic", "العربية"),
("Beveled Rectangle", "مستطيل مشطوف"),
("Chinese", "الصينية"),
("Constrain proportions for width and height", "تقييد نسب العرض والارتفاع"),
("Custom", "مخصص"),
("Czech", "التشيكية"),
("Danish", "الدنماركية"),
("Dashed", "متقطع"),
("Dotted", "منقط"),
("Dutch: 2005 Reform", "الهولندية: إصلاح 2005"),
("Embedded", "مضمّن"),
("English: Canadian", "الإنجليزية: كندا"),
("English: UK", "الإنجليزية: المملكة المتحدة"),
("English: USA", "الإنجليزية: الولايات المتحدة"),
("Farsi", "الفارسية"),
("Finnish", "الفنلندية"),
("French", "الفرنسية"),
("French: Canadian", "الفرنسية: كندا"),
("German: 2006 Reform", "الألمانية: إصلاح 2006"),
("German: Swiss 2006 Reform", "الألمانية: الإصلاح السويسري 2006"),
("Greek", "اليونانية"),
("Hebrew", "العبرية"),
("Hindi", "الهندية"),
("Hungarian", "المجرية"),
("Inverse Rounded Rectangle", "مستطيل بزوايا دائرية معكوسة"),
("Italian", "الإيطالية"),
("Japanese", "اليابانية"),
("Korean", "الكورية"),
("Make all settings the same", "توحيد جميع الإعدادات"),
("Native", "أرقام محلية"),
("Norwegian: Bokmål", "النرويجية: بوكمول"),
("Orthogonal Line", "خط متعامد"),
("Polish", "البولندية"),
("Portuguese", "البرتغالية"),
("Portuguese: Brazilian", "البرتغالية: البرازيل"),
("Rounded Rectangle", "مستطيل مستدير الزوايا"),
("Russian", "الروسية"),
("Solid", "متصل"),
("Spanish", "الإسبانية"),
("Swedish", "السويدية"),
("Thick - Thick", "سميك - سميك"),
("Thick - Thin", "سميك - رفيع"),
("Thin - Thick", "رفيع - سميك"),
("Thin - Thin", "رفيع - رفيع"),
("Thin - Thick - Thin", "رفيع - سميك - رفيع"),
("Thick - Thin - Thick", "سميك - رفيع - سميك"),
("Wavy", "متموج"),
("Straight Hash", "شرطات مائلة"),
("Stripes", "خطوط"),
("Triangle", "مثلث"),
("Turkish", "التركية"),
("Ukrainian", "الأوكرانية"),
("Cell Style", "نمط الخلية"),
("Table Style", "نمط الجدول"),
("Col ←", "إدراج عمود يسارًا"),
("Col →", "إدراج عمود يمينًا"),
("Row ↑", "إدراج صف أعلى"),
("Row ↓", "إدراج صف أسفل"),
("Hide Others", "إخفاء الطبقات الأخرى"),
("Lock Others", "قفل الطبقات الأخرى"),
("Show All Layers", "إظهار كل الطبقات"),
("Unlock", "إلغاء القفل"),
("Unlock All Layers", "إلغاء قفل كل الطبقات"),
("Added", "مضاف"),
("Deleted", "محذوف"),
("Guide-based", "استنادًا إلى الأدلة"),
("Loop", "تكرار"),
("Object-based", "استنادًا إلى الكائنات"),
("Play on Page Load", "تشغيل عند تحميل الصفحة"),
("Re-center", "إعادة التوسيط"),
("Show Controls", "إظهار عناصر التحكم"),
("Signature Field", "حقل توقيع"),
("Select text to apply a condition", "حدد نصًا لتطبيق شرط"),
("Blinds", "ستائر"),
("Box", "مربع"),
("Comb", "مشط"),
("Cover", "تغطية"),
("Dissolve", "تلاشي متناثر"),
("Fade", "تلاشي"),
("Push", "دفع"),
("Split", "انقسام"),
("Uncover", "كشف"),
("Wipe", "مسح"),
("Simple", "بسيط"),
("Simple - Wide", "بسيط - عريض"),
("Triangle - Wide", "مثلث - عريض"),
("Barbed", "مسنن"),
("Curved", "منحني"),
("Circle", "دائرة"),
("Circle - Solid", "دائرة مصمتة"),
("Square", "مربع"),
("Square - Solid", "مربع مصمت"),
("Bar", "شرطة"),
("Hide", "إخفاء"),
("Show", "إظهار"),
("Select", "تحديد"),
("Fit Text", "ملاءمة النص"),
("Preview (W)", "معاينة (W)"),
("Normal (W)", "عادي (W)"),
("Conveyor", "ناقل المحتوى"),
("objects", "كائنات"),
("pages", "صفحات"),
("stories", "نصوص"),
("items", "عناصر"),
("warnings", "تحذيرات"),
("Render", "تصيير"),
("Set", "مجموعة"),
("unused", "غير مستخدم"),
("⚠ Missing", "⚠ مفقود"),
("▲ Modified", "▲ معدّل"),
("(Text Color)", "(لون النص)"),
("{count} glyphs — click to insert at the text cursor", "عدد المحارف: {count} — انقر للإدراج عند مؤشر النص"),
("{pages} Pages in {spreads} Spreads", "عدد الصفحات: {pages}، المجموعات: {spreads}"),
("Text Frame", "إطار نص"),
("Text Style", "نمط النص"),
(" (auto)", " (تلقائي)"),
("Absolute Numbering (instead of Section Numbering)", "ترقيم مطلق بدلًا من ترقيم الأقسام"),
("Add and remove pages as the primary text frame's story grows and shrinks", "إضافة الصفحات وحذفها مع زيادة نص الإطار الرئيسي أو نقصانه"),
("Added Text (highlighted)", "النص المضاف (مميز)"),
("Adjust Layout (objects follow the new page size)", "ضبط التخطيط وفق حجم الصفحة الجديد"),
("Auto-Fit", "ملاءمة تلقائية"),
("Case sensitive", "مراعاة حالة الأحرف"),
("Custom Tracking/Kerning", "تباعد أحرف وأزواج مخصص"),
("Dimensions Include Stroke Weight", "تضمين سماكة الحد في الأبعاد"),
("Enable Autocorrect", "تمكين التصحيح التلقائي"),
("Enable Dynamic Spelling (underline misspelled words)", "تمكين التدقيق الإملائي التلقائي (تسطير الأخطاء)"),
("Grids in Back", "الشبكات في الخلف"),
("H&J Violations", "مخالفات الواصلة والضبط"),
("Include Bleed", "تضمين النزف"),
("Include Stroke Weight", "تضمين سماكة الحد"),
("Keep Violations", "مخالفات خيارات إبقاء النص"),
("Overprint [Black] Swatch at 100%", "طباعة حامل الأسود بنسبة 100% فوقيًا"),
("Printer's Marks", "علامات الطباعة"),
("Remove Styles and Formatting from Text and Tables", "إزالة الأنماط والتنسيق من النص والجداول"),
("Ruby", "روبي"),
("Show Note Anchors in Layout View", "إظهار مراسي الملاحظات في عرض التخطيط"),
("Spreads", "صفحات متقابلة"),
("Substituted Fonts", "الخطوط المستبدلة"),
("Transformations are Totals", "عرض قيم التحويل الإجمالية"),
("Use Black Point Compensation", "استخدام تعويض النقطة السوداء"),
("Use Typographer's Quotes", "استخدام علامات الاقتباس الطباعية"),
("Whole word", "كلمة كاملة"),
(" Close ", " إغلاق "),
("(Top Level)", "(المستوى الأعلى)"),
("(import)", "(استيراد)"),
("Accept All", "قبول الكل"),
("Add Document…", "إضافة مستند…"),
("Add to Previous Spread", "إضافة إلى الصفحات المتقابلة السابقة"),
("Align From:", "المحاذاة من:"),
("Align to baseline grid", "محاذاة إلى شبكة خطوط الأساس"),
("All Spots to Process", "تحويل كل الألوان الموضعية إلى ألوان معالجة"),
("Allow Spread to Shuffle", "السماح بإعادة ترتيب الصفحات المتقابلة"),
("Alternating Row Fills", "تعبئة الصفوف بالتناوب"),
("Apply Gradient", "تطبيق تدرج"),
("Apply None (/)", "تطبيق بلا لون (/)"),
("Apply Parent", "تطبيق الصفحة الأصلية"),
("Apply to All Spreads", "تطبيق على كل الصفحات المتقابلة"),
("Apply to Stroke", "تطبيق على الحد"),
("Art", "حدود الرسم"),
("Baseline Grid", "شبكة خطوط الأساس"),
("Bleed and Slug", "النزف ومنطقة المعلومات"),
("Body Rows", "صفوف المتن"),
("Break Link to Style", "فك الارتباط بالنمط"),
("Bullets and Numbering", "التعداد النقطي والرقمي"),
("Business Card", "بطاقة عمل"),
("Buttons act in interactive PDF export.", "تعمل الأزرار عند التصدير إلى PDF تفاعلي."),
("Change to:", "التغيير إلى:"),
("Character Settings", "إعدادات الأحرف"),
("Characters", "أحرف"),
("Checked by default", "محدد افتراضيًا"),
("Choices", "الخيارات"),
("Choose Image…", "اختيار صورة…"),
("Choose a link.", "اختر رابطًا."),
("Content Fitting", "ملاءمة المحتوى"),
("Convert Shape", "تحويل الشكل"),
("Copies:", "عدد النسخ:"),
("Count", "عدّ"),
("Create", "إنشاء"),
("Create Alternate Layout", "إنشاء تخطيط بديل"),
("Create Table…", "إنشاء جدول…"),
("Crop", "قص"),
("Crop Amount", "مقدار القص"),
("Crop to:", "القص إلى:"),
("Cross-Reference Format", "تنسيق المرجع المتقاطع"),
("Cursor Key:", "مفتاح المؤشر:"),
("Default Fill and Stroke (D)", "التعبئة والحد الافتراضيان (D)"),
("Default View:", "العرض الافتراضي:"),
("Delete Column", "حذف العمود"),
("Delete Condition", "حذف الشرط"),
("Delete Row", "حذف الصف"),
("Delete Swatch", "حذف حامل اللون"),
("Delete Unused Layers", "حذف الطبقات غير المستخدمة"),
("Delete Workspace", "حذف مساحة العمل"),
("Document Grid", "شبكة المستند"),
("Document Recovery Data", "بيانات استرداد المستند"),
("Duplicate Spread", "تكرار الصفحات المتقابلة"),
("Email", "بريد إلكتروني"),
("Embed", "تضمين"),
("Fast", "سريع"),
("Fill (X)", "تعبئة (X)"),
("Fill with Placeholder Text", "ملء بنص مؤقت"),
("Find All", "بحث عن الكل"),
("Find what:", "البحث عن:"),
("Fit frame height to text", "ملاءمة ارتفاع الإطار للنص"),
("Fitting:", "الملاءمة:"),
("Flip Horizontal", "قلب أفقي"),
("Flip Vertical", "قلب عمودي"),
("Font Size:", "حجم الخط:"),
("Form Field", "حقل نموذج"),
("Format:", "التنسيق:"),
("Height:", "الارتفاع:"),
("Hide Bar", "إخفاء الشريط"),
("High Quality", "جودة عالية"),
("Highlight", "تمييز"),
("Horizontal Gridline Every:", "تباعد خطوط الشبكة الأفقية:"),
("Horizontal Margins:", "الهوامش الأفقية:"),
("Import XML…", "استيراد XML…"),
("Increment Every:", "مقدار الزيادة:"),
("Ink Manager", "مدير الأحبار"),
("Insert Column", "إدراج عمود"),
("Insert Page After", "إدراج صفحة بعدها"),
("Insert Row", "إدراج صف"),
("Inset spacing", "التباعد الداخلي"),
("Intent:", "الغرض:"),
("Invert", "عكس"),
("Keyboard Increments", "زيادات لوحة المفاتيح"),
("Kind:", "النوع:"),
("Link To:", "الربط إلى:"),
("Liquid guides", "أدلة التخطيط المرن"),
("Load Profile…", "تحميل ملف تعريف…"),
("Load Swatches…", "تحميل حوامل الألوان…"),
("Load…", "تحميل…"),
("Media", "وسائط"),
("Merge into Active Layer", "دمج في الطبقة النشطة"),
("Misspelled word → Correction (one per line):", "كلمة خاطئة ← تصحيح (واحد في كل سطر):"),
("Mobile", "جهاز محمول"),
("More ArtCraft apps", "المزيد من تطبيقات ArtCraft"),
("Move Selection Here", "نقل التحديد إلى هنا"),
("Move to Color Group", "نقل إلى مجموعة ألوان"),
("Move to Group", "نقل إلى مجموعة"),
("Multiline", "متعدد الأسطر"),
("Multiple Objects", "كائنات متعددة"),
("Name:", "الاسم:"),
("New Bookmark for This Page", "إشارة مرجعية جديدة لهذه الصفحة"),
("New Book…", "كتاب جديد…"),
("New Color Group", "مجموعة ألوان جديدة"),
("New Color Group with Swatch", "مجموعة ألوان جديدة من الحامل"),
("New Library…", "مكتبة جديدة…"),
("New Workspace…", "مساحة عمل جديدة…"),
("No articles: exports follow page order.", "لا توجد مقالات؛ يتبع التصدير ترتيب الصفحات."),
("No book open.", "لا يوجد كتاب مفتوح."),
("No bookmarks. They become the PDF outline.", "لا توجد إشارات مرجعية. تظهر في مخطط PDF."),
("No changes.", "لا توجد تغييرات."),
("No conditions. Create one, then select text and click it to apply.", "لا توجد شروط. أنشئ شرطًا ثم حدد النص وانقر عليه لتطبيقه."),
("No document.", "لا يوجد مستند."),
("No hyperlinks. Select text or a frame, type a URL and click New.", "لا توجد روابط. حدد نصًا أو إطارًا وأدخل عنوان URL ثم انقر على جديد."),
("No library open.", "لا توجد مكتبة مفتوحة."),
("No notes in this document.", "لا توجد ملاحظات في هذا المستند."),
("No placed graphics.", "لا توجد رسومات مدرجة."),
("No scripts. Add one below.", "لا توجد نصوص برمجية. أضف واحدًا أدناه."),
("No spot inks", "لا توجد أحبار موضعية"),
("No text anchors in this document.", "لا توجد مراسي نصية في هذا المستند."),
("Number of Pages:", "عدد الصفحات:"),
("Number of Sides:", "عدد الأضلاع:"),
("Number of columns", "عدد الأعمدة"),
("OK changes all: text and paragraph/character styles.", "تطبّق الموافقة التغييرات على النص وأنماط الفقرات والأحرف."),
("OVERSET", "نص فائض"),
("On Release", "عند الإفلات"),
("On Screen:", "على الشاشة:"),
("Open source. No telemetry.", "مفتوح المصدر. بلا تتبع للاستخدام."),
("Options for Black on RGB and Grayscale Devices", "خيارات الأسود لأجهزة RGB والتدرج الرمادي"),
("Orientation:", "الاتجاه:"),
("Page Numbering", "ترقيم الصفحات"),
("Page · PPI", "صفحة · PPI"),
("Pages:", "الصفحات:"),
("Part of the ArtCraft suite of open creative tools.", "جزء من مجموعة ArtCraft للأدوات الإبداعية المفتوحة."),
("Pasteboard Options", "خيارات مساحة اللصق"),
("Path", "مسار"),
("Place the insertion point in a table, or create one.", "ضع نقطة الإدراج داخل جدول أو أنشئ جدولًا."),
("Plain Text", "نص عادي"),
("Plays in EPUB export; print and PDF show the poster.", "يعمل في تصدير EPUB؛ وتعرض الطباعة وPDF صورة الغلاف."),
("Poster", "صورة الغلاف"),
("Printer:", "الطابعة:"),
("Printing / Exporting:", "الطباعة / التصدير:"),
("Properties Panel", "لوحة الخصائص"),
("Recently Used", "المستخدمة مؤخرًا"),
("Reject All", "رفض الكل"),
("Relative To:", "بالنسبة إلى:"),
("Relink to Folder…", "إعادة الربط بمجلد…"),
("Relink…", "إعادة الربط…"),
("Remove Conditions from Selection", "إزالة الشروط عن التحديد"),
("Repeat Header Rows", "تكرار صفوف الرأس"),
("Replace With", "استبدال بـ"),
("Required", "مطلوب"),
("Reset All to Defaults", "إعادة تعيين الكل إلى الافتراضي"),
("Reverse", "عكس"),
("Right-click: stroke, groups", "نقرة يمنى: الحد والمجموعات"),
("Rotate 90° Clockwise", "تدوير 90° مع عقارب الساعة"),
("Ruler Units", "وحدات المسطرة"),
("Save Swatches for Exchange…", "حفظ الحوامل للتبادل…"),
("Save recovery data every:", "حفظ بيانات الاسترداد كل:"),
("Saves which bars show and where the panels are.", "يحفظ الأشرطة الظاهرة ومواضع اللوحات."),
("Search:", "البحث:"),
(
"Select a placed video or sound (File › Place a .mp4, .mov, .mp3, .wav …).",
"حدد فيديو أو صوتًا مدرجًا (ملف › إدراج ملف .mp4 أو .mov أو .mp3 أو .wav …).",
),
("Select an object to color its fill or stroke.", "حدد كائنًا لتلوين تعبئته أو حده."),
("Select an object to give its fill a gradient.", "حدد كائنًا لإضافة تدرج إلى تعبئته."),
("Select an object to make it a button.", "حدد كائنًا لتحويله إلى زر."),
("Select an object.", "حدد كائنًا."),
("Select text or a text frame.", "حدد نصًا أو إطار نص."),
("Select two or more objects (or a group) to make a multi-state object.", "حدد كائنين أو أكثر (أو مجموعة) لإنشاء كائن متعدد الحالات."),
("Show Tag Markers", "إظهار علامات الوسوم"),
("Single-line", "سطر واحد"),
("Smart Text Reflow", "إعادة تدفق النص الذكية"),
("Speed", "السرعة"),
("Star Inset:", "تقعر النجمة:"),
("Start Page #:", "رقم صفحة البداية:"),
("Start:", "البداية:"),
("Status", "الحالة"),
("Stroke (X)", "حد (X)"),
("Style Mapping", "مطابقة الأنماط"),
("Style Name Conflicts", "تعارض أسماء الأنماط"),
("Stylistic Sets", "المجموعات الأسلوبية"),
("Subdivisions:", "التقسيمات الفرعية:"),
("Swap Fill and Stroke (Shift+X)", "تبديل التعبئة والحد (Shift+X)"),
("Synchronize", "مزامنة"),
("System default", "افتراضي النظام"),
("Table Dimensions", "أبعاد الجدول"),
("Text Display Options", "خيارات عرض النص"),
("Text Message", "رسالة نصية"),
("The fill isn't a gradient.", "التعبئة ليست تدرجًا."),
("The library is empty. Select objects and Add Selection.", "المكتبة فارغة. حدد كائنات ثم أضف التحديد."),
("This PDF has no layers.", "ملف PDF هذا لا يحتوي على طبقات."),
("Track Changes in All Stories", "تتبع التغييرات في جميع النصوص"),
("Transitions play in interactive PDF (full-screen mode).", "تعمل الانتقالات في PDF التفاعلي بوضع ملء الشاشة."),
("Trim", "حدود التشذيب"),
("Type Options", "خيارات النص"),
("Typical", "نموذجي"),
("UI Size:", "حجم الواجهة:"),
("Ungroup Color Group", "فك مجموعة الألوان"),
("Unmerge", "إلغاء الدمج"),
("Update Numbering", "تحديث الترقيم"),
("Use Global Light", "استخدام الإضاءة العامة"),
("User Dictionary (this document)", "قاموس المستخدم (هذا المستند)"),
("Variable Font", "خط متغير"),
("Vertical Gridline Every:", "تباعد خطوط الشبكة العمودية:"),
("Vertical Margins:", "الهوامش العمودية:"),
("Vertical justification", "المحاذاة العمودية"),
("View Threshold:", "حد العرض:"),
("Web", "ويب"),
("Web Hyperlink", "رابط ويب"),
("When Scaling", "عند التحجيم"),
("Words, one per line:", "الكلمات، واحدة في كل سطر:"),
("[Basic] (working) ▾", "[أساسي] (قيد العمل) ▾"),
("[Black] Overprint", "طباعة [الأسود] فوقيًا"),
("[No Group]", "[بلا مجموعة]"),
("[None] — choose a colour on the ramp.", "[بلا] — اختر لونًا على شريط التدرج."),
("⌥-click clears overrides", "النقر مع ⌥ يمسح التجاوزات"),
("⚠ out of date", "⚠ غير محدّث"),
("Interface", "الواجهة"),
("Advanced Type", "إعدادات النص المتقدمة"),
("Composition", "تنضيد النص"),
("Units & Increments", "الوحدات والزيادات"),
("Grids", "الشبكات"),
("Guides & Pasteboard", "الأدلة ولوحة اللصق"),
("Dictionary", "القاموس"),
("Spelling", "التدقيق الإملائي"),
("Autocorrect", "التصحيح التلقائي"),
("Track Changes", "تعقب التغييرات"),
("Story Editor Display", "عرض محرر النص"),
("Display Performance", "أداء العرض"),
("Appearance of Black", "مظهر الأسود"),
("File Handling", "معالجة الملفات"),
("Formatting affects container (J)", "تطبيق التنسيق على الحاوية (J)"),
("Formatting affects text (J)", "تطبيق التنسيق على النص (J)"),
("Apply Color", "تطبيق اللون"),
("Apply None", "إزالة اللون"),
("Height Only", "الارتفاع فقط"),
("Width Only", "العرض فقط"),
("Height and Width", "الارتفاع والعرض"),
("Height only", "الارتفاع فقط"),
("Width only", "العرض فقط"),
("Height and width", "الارتفاع والعرض"),
("Version", "الإصدار"),
("{count} characters don't fit", "عدد الأحرف الزائدة: {count}"),
("document", "المستند"),
("story", "النص"),
("selection", "التحديد"),
("top", "أعلى"),
("center", "وسط"),
("bottom", "أسفل"),
("justify", "ضبط"),
("text", "نص"),
("graphic", "رسم"),
("unassigned", "غير معين"),
("Perceptual", "إدراكي"),
("Relative Colorimetric", "قياس لوني نسبي"),
("Saturation", "التشبع"),
("Absolute Colorimetric", "قياس لوني مطلق"),
("No conflicts", "لا توجد تعارضات"),
("Page (1–{count}):", "الصفحة (1–{count}):"),
("Fonts in Document: {count} Missing: {missing}", "الخطوط في المستند: {count} المفقودة: {missing}"),
];
+5 -1
View File
@@ -1902,7 +1902,11 @@ pub fn palette(app: &mut DesignApp, ctx: &egui::Context) {
let mut run: Option<(String, Value)> = None;
egui::Modal::new(egui::Id::new("palette")).show(ctx, |ui| {
ui.set_width(480.0);
let r = ui.add(egui::TextEdit::singleline(&mut q).hint_text("Search styles and commands…").desired_width(f32::INFINITY));
let r = ui.add(
egui::TextEdit::singleline(&mut q)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Search styles and commands…")))
.desired_width(f32::INFINITY),
);
r.request_focus();
let items = quick_apply_items(&app.session, &q);
egui::ScrollArea::vertical().max_height(340.0).show(ui, |ui| {
+8 -2
View File
@@ -28,7 +28,9 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let (row, _) = ui.allocate_exact_size(vec2(ui.available_width(), 22.0), Sense::hover());
// Visibility (eye).
let eye = egui::Rect::from_min_size(row.min + vec2(2.0, 3.0), vec2(16.0, 16.0));
let er = ui.interact(eye, ui.id().with(("cond_eye", &c.name)), Sense::click()).on_hover_text("Show / hide text with this condition");
let er = ui.interact(eye, ui.id().with(("cond_eye", &c.name)), Sense::click()).on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Show / hide text with this condition"));
});
if c.visible {
crate::icons::paint(ui.painter(), eye, "eye", t.text_dim);
}
@@ -73,7 +75,11 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let id = egui::Id::new("new_condition_name");
let mut name: String = ui.data(|d| d.get_temp(id)).unwrap_or_default();
ui.horizontal(|ui| {
ui.add(egui::TextEdit::singleline(&mut name).hint_text("New condition name").desired_width(150.0));
ui.add(
egui::TextEdit::singleline(&mut name)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New condition name")))
.desired_width(150.0),
);
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New"))).clicked() && !name.trim().is_empty() {
match app.run("condition.new", json!({"name": name.trim()})) {
Ok(_) => name.clear(),
+13 -2
View File
@@ -50,7 +50,11 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
}
});
});
ui.add(egui::TextEdit::singleline(&mut st.query).hint_text("Search: character or U+code").desired_width(f32::INFINITY));
ui.add(
egui::TextEdit::singleline(&mut st.query)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Search: character or U+code")))
.desired_width(f32::INFINITY),
);
let face = db.face(&st.family, &st.style);
let q = st.query.trim().to_string();
let code = q.strip_prefix("U+").or_else(|| q.strip_prefix("u+")).and_then(|h| u32::from_str_radix(h, 16).ok());
@@ -129,6 +133,13 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
Err(e) => app.status(format!("Glyphs: {e}")),
}
}
ui.label(egui::RichText::new(format!("{} glyphs — click to insert at the text cursor", chars.len())).size(10.5).color(t.text_dim));
crate::rtl::label(
ui,
egui::RichText::new(
crate::i18n::tr(&app.ui.language, "{count} glyphs — click to insert at the text cursor").replace("{count}", &chars.len().to_string()),
)
.size(10.5)
.color(t.text_dim),
);
ui.data_mut(|d| d.insert_temp(id, st));
}
+205 -98
View File
@@ -25,7 +25,13 @@ pub fn hyperlinks(app: &mut DesignApp, ui: &mut egui::Ui) {
let has_target = app.session.active().is_some_and(|d| d.selection.text.is_some_and(|t| !t.is_caret()) || !d.selection.items.is_empty());
ui.horizontal(|ui| {
ui.add(egui::TextEdit::singleline(&mut url).desired_width(170.0));
if ui.add_enabled(has_target, egui::Button::new("New")).on_hover_text("Hyperlink from the selected text or frames").clicked() {
if ui
.add_enabled(has_target, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New"))))
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Hyperlink from the selected text or frames"));
})
.clicked()
{
let p = if let Some(m) = url.strip_prefix("mailto:") { json!({"email": m}) } else { json!({"url": url}) };
if let Err(e) = app.run("hyperlink.create", p) {
app.status(format!("Hyperlinks: {e}"));
@@ -36,12 +42,15 @@ pub fn hyperlinks(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.separator();
let list = app.session.execute("hyperlink.list", &json!({})).ok().and_then(|v| v.as_array().cloned()).unwrap_or_default();
if list.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No hyperlinks. Select text or a frame, type a URL and click New."))
.size(11.0)
.color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No hyperlinks. Select text or a frame, type a URL and click New."))
.size(11.0)
.color(t.text_dim),
),
);
}
for h in list {
let hid = h["id"].clone();
@@ -51,7 +60,7 @@ pub fn hyperlinks(app: &mut DesignApp, ui: &mut egui::Ui) {
let _ = app.run("hyperlink.goToSource", json!({"id": hid}));
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("hyperlink.delete", json!({"id": hid}));
}
});
@@ -68,10 +77,13 @@ pub fn bookmarks(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.separator();
let list = app.session.execute("bookmark.list", &json!({})).ok().and_then(|v| v.as_array().cloned()).unwrap_or_default();
if list.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No bookmarks. They become the PDF outline.")).size(11.0).color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No bookmarks. They become the PDF outline.")).size(11.0).color(t.text_dim),
),
);
}
for (i, b) in list.iter().enumerate() {
let page = b["page"].as_u64().unwrap_or(0) as usize;
@@ -81,7 +93,7 @@ pub fn bookmarks(app: &mut DesignApp, ui: &mut egui::Ui) {
crate::canvas::go_to_page(app, page);
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("bookmark.delete", json!({"index": i}));
}
});
@@ -93,31 +105,51 @@ pub fn bookmarks(app: &mut DesignApp, ui: &mut egui::Ui) {
pub fn articles(app: &mut DesignApp, ui: &mut egui::Ui) {
let t = Tokens::get(ui.ctx());
let has_sel = app.session.active().is_some_and(|d| !d.selection.items.is_empty());
if ui.add_enabled(has_sel, egui::Button::new("New Article from Selection")).clicked() {
if ui.add_enabled(has_sel, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New Article from Selection")))).clicked() {
let _ = app.run("article.new", json!({}));
}
ui.separator();
let list = app.session.execute("article.list", &json!({})).ok().and_then(|v| v.as_array().cloned()).unwrap_or_default();
if list.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No articles: exports follow page order.")).size(11.0).color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No articles: exports follow page order.")).size(11.0).color(t.text_dim),
),
);
}
for a in list {
let name = a["name"].as_str().unwrap_or("").to_string();
let mut export = a["export"].as_bool().unwrap_or(true);
ui.horizontal(|ui| {
if ui.checkbox(&mut export, "").on_hover_text("Include when exporting").changed() {
if ui
.checkbox(&mut export, "")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Include when exporting"));
})
.changed()
{
let _ = app.run("article.options", json!({"name": name, "export": export}));
}
ui.label(egui::RichText::new(&name).strong());
ui.label(egui::RichText::new(format!("{} objects", a["items"].as_array().map_or(0, Vec::len))).size(10.5).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(&name).strong());
crate::rtl::label(
ui,
egui::RichText::new(format!("{} {}", a["items"].as_array().map_or(0, Vec::len), crate::i18n::tr(&app.ui.language, "objects")))
.size(10.5)
.color(t.text_dim),
);
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("article.delete", json!({"name": name}));
}
if ui.add_enabled(has_sel, egui::Button::new("Add").small()).on_hover_text("Add the selection").clicked() {
if ui
.add_enabled(has_sel, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Add"))).small())
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Add the selection"));
})
.clicked()
{
let _ = app.run("article.add", json!({"name": name}));
}
});
@@ -136,7 +168,11 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
let id = egui::Id::new("new_tag_name");
let mut name: String = ui.data(|d| d.get_temp(id)).unwrap_or_default();
ui.horizontal(|ui| {
ui.add(egui::TextEdit::singleline(&mut name).hint_text("New tag").desired_width(140.0));
ui.add(
egui::TextEdit::singleline(&mut name)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New tag")))
.desired_width(140.0),
);
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New"))).clicked() && !name.trim().is_empty() {
match app.run("xml.newTag", json!({"name": name.trim()})) {
Ok(_) => name.clear(),
@@ -153,17 +189,23 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
if c.len() == 3 {
ui.painter().rect_filled(r, 2.0, egui::Color32::from_rgb(c[0], c[1], c[2]));
}
if ui.add_enabled(has_sel || text_sel, egui::Button::new(&n).frame(false)).on_hover_text("Tag the selection").clicked() {
if ui
.add_enabled(has_sel || text_sel, egui::Button::new(&n).frame(false))
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Tag the selection"));
})
.clicked()
{
let _ = if text_sel { app.run("xml.tagText", json!({"tag": n})) } else { app.run("xml.tag", json!({"tag": n})) };
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("xml.deleteTag", json!({"name": n}));
}
});
});
}
if (has_sel || text_sel) && ui.small_button("Untag Selection").clicked() {
if (has_sel || text_sel) && ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Untag Selection"))).clicked() {
let _ = if text_sel { app.run("xml.tagText", json!({"tag": null})) } else { app.run("xml.tag", json!({"tag": null})) };
}
let mut markers = app.ui.tag_markers;
@@ -171,9 +213,9 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
let _ = app.run("view.tagMarkers", json!({"on": markers}));
}
ui.separator();
ui.label(egui::RichText::new("Structure").strong());
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Structure")).strong());
let st = app.session.execute("xml.structure", &json!({})).unwrap_or_default();
ui.label(egui::RichText::new(st["root"].as_str().unwrap_or("Root")).size(11.0).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(st["root"].as_str().unwrap_or("Root")).size(11.0).color(t.text_dim));
for el in st["elements"].as_array().cloned().unwrap_or_default() {
let text = el["text"].as_str().unwrap_or("").chars().take(40).collect::<String>();
let label = format!(" <{}> {}", el["tag"].as_str().unwrap_or(""), text);
@@ -185,7 +227,7 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
let vid = egui::Id::new("dtd_problems");
let mut problems: Option<Vec<String>> = ui.data(|d| d.get_temp(vid));
ui.horizontal(|ui| {
if ui.button("Load DTD…").clicked()
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Load DTD…"))).clicked()
&& let Some(path) = app.services.pick_open.as_mut().and_then(|f| f("dtd"))
{
match app.run("xml.loadDtd", json!({"path": path})) {
@@ -194,7 +236,7 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
}
}
let has_dtd = info["dtd"].as_bool().unwrap_or(false);
if ui.add_enabled(has_dtd, egui::Button::new("Validate")).clicked()
if ui.add_enabled(has_dtd, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Validate")))).clicked()
&& let Ok(v) = app.session.execute("xml.validate", &json!({}))
{
problems = Some(
@@ -206,18 +248,18 @@ pub fn tags(app: &mut DesignApp, ui: &mut egui::Ui) {
.collect(),
);
}
if has_dtd && ui.small_button("Delete DTD").clicked() {
if has_dtd && ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete DTD"))).clicked() {
let _ = app.run("xml.deleteDtd", json!({}));
problems = None;
}
});
match &problems {
Some(list) if list.is_empty() => {
ui.label(egui::RichText::new("The structure is valid.").size(11.0).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "The structure is valid.")).size(11.0).color(t.text_dim));
}
Some(list) => {
for p in list {
ui.label(egui::RichText::new(format!("⚠ {p}")).size(11.0));
crate::rtl::label(ui, egui::RichText::new(format!("⚠ {p}")).size(11.0));
}
}
None => {}
@@ -251,13 +293,21 @@ pub fn states(app: &mut DesignApp, ui: &mut egui::Ui) {
let info = app.session.execute("states.list", &json!({})).ok();
let list = info.as_ref().and_then(|i| i["states"].as_array().cloned()).unwrap_or_default();
if list.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select two or more objects (or a group) to make a multi-state object."))
.size(11.0)
.color(t.text_dim),
));
if ui.add_enabled(n > 0, egui::Button::new("Convert Selection to Multi-State Object")).clicked()
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select two or more objects (or a group) to make a multi-state object."))
.size(11.0)
.color(t.text_dim),
),
);
if ui
.add_enabled(
n > 0,
egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Convert Selection to Multi-State Object"))),
)
.clicked()
&& let Err(e) = app.run("states.create", json!({}))
{
app.status(format!("Object States: {e}"));
@@ -271,7 +321,7 @@ pub fn states(app: &mut DesignApp, ui: &mut egui::Ui) {
}
}
ui.separator();
if ui.small_button("Release to Objects").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Release to Objects"))).clicked() {
let _ = app.run("states.release", json!({}));
}
}
@@ -280,10 +330,13 @@ pub fn states(app: &mut DesignApp, ui: &mut egui::Ui) {
pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
let t = Tokens::get(ui.ctx());
let Some(id) = app.session.active().and_then(|d| d.selection.items.first().copied()) else {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select an object to make it a button.")).size(11.0).color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select an object to make it a button.")).size(11.0).color(t.text_dim),
),
);
return;
};
let cur = app.session.active().and_then(|d| d.doc.item(id)).and_then(|it| it.button.clone());
@@ -309,13 +362,15 @@ pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
let mut pick = None;
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "On Release"));
egui::ComboBox::from_id_salt("button_action").selected_text(labels.iter().find(|l| l.0 == kind).map_or("", |l| l.1)).show_ui(ui, |ui| {
for (k, l) in labels {
if ui.selectable_label(k == kind, l).clicked() {
pick = Some(k);
egui::ComboBox::from_id_salt("button_action")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, labels.iter().find(|l| l.0 == kind).map_or("", |l| l.1))))
.show_ui(ui, |ui| {
for (k, l) in labels {
if ui.selectable_label(k == kind, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
pick = Some(k);
}
}
}
});
});
});
let mut params = None;
if let Some(k) = pick {
@@ -345,10 +400,13 @@ pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
{
app.status(format!("Buttons: {e}"));
}
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Buttons act in interactive PDF export.")).size(10.5).color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Buttons act in interactive PDF export.")).size(10.5).color(t.text_dim),
),
);
// Form field.
ui.separator();
let ff = app.session.active().and_then(|d| d.doc.item(id)).and_then(|it| it.form_field.clone());
@@ -364,16 +422,18 @@ pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
let cur = ff.as_ref().map(|f| f.kind);
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Form Field"));
egui::ComboBox::from_id_salt("form_kind").selected_text(kinds.iter().find(|k| k.0 == cur).map_or("", |k| k.1)).show_ui(ui, |ui| {
for (k, l) in kinds {
if ui.selectable_label(k == cur, l).clicked() {
let _ = match k {
Some(k) => app.run("form.set", json!({"kind": k})),
None => app.run("form.clear", json!({})),
};
egui::ComboBox::from_id_salt("form_kind")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, kinds.iter().find(|k| k.0 == cur).map_or("", |k| k.1))))
.show_ui(ui, |ui| {
for (k, l) in kinds {
if ui.selectable_label(k == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
let _ = match k {
Some(k) => app.run("form.set", json!({"kind": k})),
None => app.run("form.clear", json!({})),
};
}
}
}
});
});
});
if let Some(f) = ff {
let key = egui::Id::new(("form_edit", id.0));
@@ -398,7 +458,12 @@ pub fn buttons(app: &mut DesignApp, ui: &mut egui::Ui) {
if matches!(f.kind, K::ComboBox | K::ListBox) {
ui.horizontal(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Choices"));
commit |= ui.text_edit_singleline(&mut opts).on_hover_text("Separated by commas").lost_focus();
commit |= ui
.text_edit_singleline(&mut opts)
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Separated by commas"));
})
.lost_focus();
});
}
let mut req = f.required;
@@ -433,15 +498,29 @@ pub fn liquid(app: &mut DesignApp, ui: &mut egui::Ui) {
let Some((si, pi)) = d.page_loc(page) else { return };
let pg = d.spreads[si].pages[pi].clone();
use designcraft_doc::LiquidRule as L;
ui.label(egui::RichText::new(format!("Page {}", d.page_name(page))).strong());
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(format!("{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")]
{
ui.selectable_value(&mut rule, r, label);
}
});
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",
},
),
))
.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")]
{
ui.selectable_value(&mut rule, r, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label)));
}
});
if rule != pg.liquid {
let _ = app.run("liquid.pageRule", json!({"rule": rule, "pages": [page + 1]}));
}
@@ -456,7 +535,7 @@ pub fn liquid(app: &mut DesignApp, ui: &mut egui::Ui) {
let labels = ["Resize width", "Resize height", "Pin top", "Pin bottom", "Pin left", "Pin right"];
ui.horizontal_wrapped(|ui| {
for ((on, key), label) in v.iter_mut().zip(keys).zip(labels) {
if ui.checkbox(on, label).changed() {
if ui.checkbox(on, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).changed() {
let _ = app.run("liquid.object", json!({key: *on}));
}
}
@@ -495,18 +574,21 @@ pub fn liquid(app: &mut DesignApp, ui: &mut egui::Ui) {
pub fn media(app: &mut DesignApp, ui: &mut egui::Ui) {
let t = Tokens::get(ui.ctx());
let Ok(m) = app.session.execute("media.get", &json!({})) else {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select a placed video or sound (File › Place a .mp4, .mov, .mp3, .wav …)."))
.size(11.0)
.color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Select a placed video or sound (File › Place a .mp4, .mov, .mp3, .wav …)."))
.size(11.0)
.color(t.text_dim),
),
);
return;
};
ui.label(egui::RichText::new(format!("{} — {}", m["kind"].as_str().unwrap_or(""), m["name"].as_str().unwrap_or(""))).strong());
crate::rtl::label(ui, egui::RichText::new(format!("{} — {}", m["kind"].as_str().unwrap_or(""), m["name"].as_str().unwrap_or(""))).strong());
for (key, label) in [("playOnPageLoad", "Play on Page Load"), ("loop", "Loop"), ("controls", "Show Controls")] {
let mut on = m[key].as_bool().unwrap_or(false);
if ui.checkbox(&mut on, label).changed() {
if ui.checkbox(&mut on, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).changed() {
let _ = app.run("media.options", json!({key: on}));
}
}
@@ -518,14 +600,19 @@ pub fn media(app: &mut DesignApp, ui: &mut egui::Ui) {
{
app.status(format!("Media: {e}"));
}
if !m["poster"].is_null() && ui.small_button("None").clicked() {
if !m["poster"].is_null() && ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "None"))).clicked() {
let _ = app.run("media.options", json!({"poster": null}));
}
});
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Plays in EPUB export; print and PDF show the poster.")).size(10.5).color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Plays in EPUB export; print and PDF show the poster."))
.size(10.5)
.color(t.text_dim),
),
);
}
/// Page Transitions panel: the current spread's transition for interactive PDF.
@@ -536,14 +623,16 @@ pub fn transitions(app: &mut DesignApp, ui: &mut egui::Ui) {
let Some((si, _)) = d.page_loc(page) else { return };
let cur = d.spreads[si].pages.first().and_then(|p| p.transition);
use designcraft_doc::TransitionKind as K;
ui.label(egui::RichText::new(format!("Spread {}", si + 1)).strong());
crate::rtl::label(ui, egui::RichText::new(format!("{} {}", crate::i18n::tr(&app.ui.language, "Spread"), si + 1)).strong());
let mut kind = cur.map(|c| c.kind);
egui::ComboBox::from_id_salt("transition_kind").selected_text(kind.map_or("None", K::label)).show_ui(ui, |ui| {
ui.selectable_value(&mut kind, None, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "None")));
for k in K::ALL {
ui.selectable_value(&mut kind, Some(k), k.label());
}
});
egui::ComboBox::from_id_salt("transition_kind")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, kind.map_or("None", K::label))))
.show_ui(ui, |ui| {
ui.selectable_value(&mut kind, None, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "None")));
for k in K::ALL {
ui.selectable_value(&mut kind, Some(k), crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, k.label())));
}
});
let mut duration = cur.map_or(1.0, |c| c.duration);
let mut horizontal = cur.is_some_and(|c| c.horizontal);
let mut changed = kind != cur.map(|c| c.kind);
@@ -553,7 +642,12 @@ pub fn transitions(app: &mut DesignApp, ui: &mut egui::Ui) {
changed |= ui.add(egui::DragValue::new(&mut duration).range(0.1..=10.0).speed(0.1).suffix(" s")).changed();
});
if matches!(kind, Some(K::Blinds | K::Box | K::Split)) {
changed |= ui.checkbox(&mut horizontal, if kind == Some(K::Box) { "Inward" } else { "Horizontal" }).changed();
changed |= ui
.checkbox(
&mut horizontal,
crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, if kind == Some(K::Box) { "Inward" } else { "Horizontal" })),
)
.changed();
}
}
let params = |all: bool| {
@@ -570,12 +664,15 @@ pub fn transitions(app: &mut DesignApp, ui: &mut egui::Ui) {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Apply to All Spreads"))).clicked() {
let _ = app.run("page.transition", params(true));
}
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Transitions play in interactive PDF (full-screen mode)."))
.size(10.5)
.color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Transitions play in interactive PDF (full-screen mode)."))
.size(10.5)
.color(t.text_dim),
),
);
}
/// Track Changes panel: tracking on/off and each change with Accept / Reject.
@@ -598,18 +695,28 @@ pub fn track_changes(app: &mut DesignApp, ui: &mut egui::Ui) {
let kind = c["kind"].as_str().unwrap_or("");
let text: String = c["text"].as_str().unwrap_or("").chars().take(40).collect();
ui.horizontal(|ui| {
let label =
egui::RichText::new(format!("{} “{}”", if kind == "inserted" { "Added" } else { "Deleted" }, text.replace('\n', ""))).size(11.0);
let label = egui::RichText::new(format!(
"{} “{}”",
crate::i18n::tr(&app.ui.language, if kind == "inserted" { "Added" } else { "Deleted" }),
text.replace('\n', "")
))
.size(11.0);
let label = if kind == "deleted" { label.strikethrough() } else { label };
if ui.selectable_label(false, label).on_hover_text("Show in the text").clicked() {
if ui
.selectable_label(false, label)
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Show in the text"));
})
.clicked()
{
let end = c["end"].clone();
let _ = app.run("text.select", json!({"story": story, "anchor": start, "focus": end}));
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Reject").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Reject"))).clicked() {
let _ = app.run("changes.reject", json!({"story": story, "start": start}));
}
if ui.small_button("Accept").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Accept"))).clicked() {
let _ = app.run("changes.accept", json!({"story": story, "start": start}));
}
});
+35 -12
View File
@@ -49,7 +49,15 @@ fn c32(c: [u8; 3]) -> Color32 {
}
/// Eye and lock cells (with column rules). Returns (eye clicked, lock clicked).
fn eye_lock(ui: &mut Ui, row: Rect, visible: bool, locked: bool, dim: bool, key: impl std::hash::Hash + std::fmt::Debug + Copy) -> (bool, bool) {
fn eye_lock(
ui: &mut Ui,
language: &str,
row: Rect,
visible: bool,
locked: bool,
dim: bool,
key: impl std::hash::Hash + std::fmt::Debug + Copy,
) -> (bool, bool) {
let t = Tokens::get(ui.ctx());
let eye = Rect::from_min_size(row.min, vec2(COL_W, ROW_H));
let lock = Rect::from_min_size(row.min + vec2(COL_W, 0.0), vec2(COL_W, ROW_H));
@@ -63,13 +71,25 @@ fn eye_lock(ui: &mut Ui, row: Rect, visible: bool, locked: bool, dim: bool, key:
if locked {
icons::paint(ui.painter(), Rect::from_center_size(lock.center(), vec2(13.0, 13.0)), "lock", col);
}
let e = ui.interact(eye, ui.id().with(("eye", key)), Sense::click()).on_hover_text(if visible { "Hide" } else { "Show" });
let l = ui.interact(lock, ui.id().with(("lock", key)), Sense::click()).on_hover_text(if locked { "Unlock" } else { "Lock" });
let e = ui.interact(eye, ui.id().with(("eye", key)), Sense::click()).on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(language, if visible { "Hide" } else { "Show" }));
});
let l = ui.interact(lock, ui.id().with(("lock", key)), Sense::click()).on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(language, if locked { "Unlock" } else { "Lock" }));
});
(e.clicked(), l.clicked())
}
/// The selection square at the right of a row: filled = selected; hollow on hover.
fn selection_square(ui: &mut Ui, row: Rect, color: Color32, selected: bool, small: bool, key: impl std::hash::Hash + std::fmt::Debug) -> bool {
fn selection_square(
ui: &mut Ui,
language: &str,
row: Rect,
color: Color32,
selected: bool,
small: bool,
key: impl std::hash::Hash + std::fmt::Debug,
) -> bool {
let s = if small { 7.0 } else { 8.0 };
let r = Rect::from_center_size(pos2(row.max.x - 12.0, row.center().y), vec2(s, s));
let resp = ui.interact(r.expand(4.0), ui.id().with(("selsq", key)), Sense::click());
@@ -78,7 +98,10 @@ fn selection_square(ui: &mut Ui, row: Rect, color: Color32, selected: bool, smal
} else if resp.hovered() {
ui.painter().rect_stroke(r, 0.0, Stroke::new(1.0, color), StrokeKind::Inside);
}
resp.on_hover_text("Select").clicked()
resp.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(language, "Select"));
})
.clicked()
}
pub fn show(app: &mut DesignApp, ui: &mut Ui) {
@@ -103,7 +126,7 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
} else if resp.hovered() {
ui.painter().rect_filled(row, 0.0, t.hover);
}
let (eye, lock) = eye_lock(ui, row, l.visible, l.locked, false, ("layer", l.id.0));
let (eye, lock) = eye_lock(ui, &app.ui.language, row, l.visible, l.locked, false, ("layer", l.id.0));
// Disclosure triangle.
let tri = Rect::from_min_size(row.min + vec2(2.0 * COL_W + 2.0, 0.0), vec2(14.0, ROW_H));
let c = tri.center();
@@ -127,7 +150,7 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
if l.id == active {
icons::paint(ui.painter(), Rect::from_center_size(pos2(row.max.x - 32.0, row.center().y), vec2(13.0, 13.0)), "tool-pen", t.icon);
}
let sq = selection_square(ui, row, lc, sel_layers.contains(&l.id), false, ("layer", l.id.0));
let sq = selection_square(ui, &app.ui.language, row, lc, sel_layers.contains(&l.id), false, ("layer", l.id.0));
if eye {
let _ = app.run("layer.set", json!({"id": l.id.0, "visible": !l.visible}));
} else if lock {
@@ -163,7 +186,7 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
("Show All Layers", json!({"id": l.id.0, "show": true})),
("Unlock All Layers", json!({"id": l.id.0, "unlock": true})),
] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("layer.others", params);
ui.close();
}
@@ -182,7 +205,7 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
if resp.hovered() {
ui.painter().rect_filled(row, 0.0, t.hover);
}
let (eye, lock) = eye_lock(ui, row, !it.hidden, it.locked, !l.visible, ("item", it.id.0));
let (eye, lock) = eye_lock(ui, &app.ui.language, row, !it.hidden, it.locked, !l.visible, ("item", it.id.0));
let name = item_label(&doc, it);
let clip = Rect::from_min_max(row.min, pos2(row.max.x - 26.0, row.max.y));
ui.painter().with_clip_rect(clip).text(
@@ -192,7 +215,7 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
egui::FontId::proportional(11.0),
if it.hidden { t.text_dim } else { t.text },
);
let sq = selection_square(ui, row, lc, is_sel, true, ("item", it.id.0));
let sq = selection_square(ui, &app.ui.language, row, lc, is_sel, true, ("item", it.id.0));
if eye {
let _ = app.run("object.setFlags", json!({"ids": [it.id.0], "hidden": !it.hidden}));
} else if lock {
@@ -217,10 +240,10 @@ pub fn show(app: &mut DesignApp, ui: &mut Ui) {
.color(t.text_dim),
);
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if icons::button(ui, "trash", 20.0, false, "Delete Layer").clicked() {
if icons::button(ui, "trash", 20.0, false, crate::i18n::tr(&app.ui.language, "Delete Layer")).clicked() {
let _ = app.run("layer.delete", json!({"id": active.0}));
}
if icons::button(ui, "plus", 20.0, false, "Create New Layer").clicked() {
if icons::button(ui, "plus", 20.0, false, crate::i18n::tr(&app.ui.language, "Create New Layer")).clicked() {
let _ = app.run("layer.new", json!({}));
}
});
+51 -29
View File
@@ -10,20 +10,23 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let t = Tokens::get(ui.ctx());
// Content Collector conveyor.
let conveyor = app.session.conveyor.iter().map(|c| c.0.clone()).collect::<Vec<_>>();
ui.label(egui::RichText::new(format!("Conveyor ({})", conveyor.len())).strong());
crate::rtl::label(ui, egui::RichText::new(format!("{} ({})", crate::i18n::tr(&app.ui.language, "Conveyor"), conveyor.len())).strong());
if conveyor.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(
&app.ui.language,
"Collect objects with the Content Collector (B); place them with the Content Placer.",
))
.size(10.5)
.color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(
&app.ui.language,
"Collect objects with the Content Collector (B); place them with the Content Placer.",
))
.size(10.5)
.color(t.text_dim),
),
);
} else {
ui.label(egui::RichText::new(conveyor.join(", ")).size(10.5));
if ui.small_button("Clear").clicked() {
crate::rtl::label(ui, egui::RichText::new(conveyor.join(", ")).size(10.5));
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Clear"))).clicked() {
let _ = app.run("conveyor.clear", json!({}));
}
}
@@ -45,7 +48,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
return;
};
let has_sel = app.session.active().is_some_and(|d| !d.selection.items.is_empty());
if ui.add_enabled(has_sel, egui::Button::new("Add Selection")).clicked()
if ui.add_enabled(has_sel, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Add Selection")))).clicked()
&& let Err(e) = app.run("library.add", json!({}))
{
app.status(format!("Library: {e}"));
@@ -53,22 +56,25 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.separator();
let items = items.as_array().cloned().unwrap_or_default();
if items.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "The library is empty. Select objects and Add Selection."))
.size(11.0)
.color(t.text_dim),
));
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "The library is empty. Select objects and Add Selection."))
.size(11.0)
.color(t.text_dim),
),
);
}
for it in items {
let i = it["index"].clone();
ui.horizontal(|ui| {
ui.label(it["name"].as_str().unwrap_or(""));
crate::rtl::label(ui, it["name"].as_str().unwrap_or(""));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("library.remove", json!({"index": i}));
}
if ui.small_button("Place").clicked()
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Place"))).clicked()
&& let Err(e) = app.run("library.place", json!({"index": i}))
{
app.status(format!("Library: {e}"));
@@ -107,15 +113,27 @@ pub fn book(app: &mut DesignApp, ui: &mut egui::Ui) {
let name = std::path::Path::new(&path).file_stem().map(|n| n.to_string_lossy().to_string()).unwrap_or_default();
let (first, pages) = (d["firstPage"].as_u64().unwrap_or(1), d["pages"].as_u64().unwrap_or(0));
ui.horizontal(|ui| {
if ui.selectable_label(i == source, if i == source { "◆" } else { "◇" }).on_hover_text("Style source").clicked() {
if ui
.selectable_label(i == source, if i == source { "◆" } else { "◇" })
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Style source"));
})
.clicked()
{
let _ = app.run("book.styleSource", json!({"index": i}));
}
if ui.selectable_label(false, &name).on_hover_text("Open").clicked() {
if ui
.selectable_label(false, &name)
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Open"));
})
.clicked()
{
let _ = app.run("file.open", json!({"path": path}));
}
ui.label(egui::RichText::new(format!("{first}–{}", first + pages.saturating_sub(1))).size(10.5).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(format!("{first}–{}", first + pages.saturating_sub(1))).size(10.5).color(t.text_dim));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui.small_button("Remove").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Remove"))).clicked() {
let _ = app.run("book.remove", json!({"index": i}));
}
});
@@ -150,10 +168,10 @@ pub fn scripts(app: &mut DesignApp, ui: &mut egui::Ui) {
let sel_id = egui::Id::new("scripts_sel");
let mut sel: usize = ui.data(|d| d.get_temp(sel_id)).unwrap_or(0);
if app.ui.scripts.is_empty() {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "No scripts. Add one below.")).size(11.0).color(t.text_dim),
));
crate::rtl::widget(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "No scripts. Add one below.")).size(11.0).color(t.text_dim)),
);
}
let mut run: Option<usize> = None;
for (i, (name, _)) in app.ui.scripts.iter().enumerate() {
@@ -167,7 +185,7 @@ pub fn scripts(app: &mut DesignApp, ui: &mut egui::Ui) {
}
ui.separator();
ui.horizontal(|ui| {
if ui.add_enabled(!app.ui.scripts.is_empty(), egui::Button::new("Run")).clicked() {
if ui.add_enabled(!app.ui.scripts.is_empty(), egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Run")))).clicked() {
run = Some(sel);
}
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New"))).clicked() {
@@ -187,7 +205,11 @@ pub fn scripts(app: &mut DesignApp, ui: &mut egui::Ui) {
Err(e) => app.status(format!("Scripts: {e}")),
}
}
if ui.add_enabled(!app.ui.scripts.is_empty(), egui::Button::new("Delete")).clicked() && sel < app.ui.scripts.len() {
if ui
.add_enabled(!app.ui.scripts.is_empty(), egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))))
.clicked()
&& sel < app.ui.scripts.len()
{
app.ui.scripts.remove(sel);
sel = sel.saturating_sub(1);
}
+12 -5
View File
@@ -201,8 +201,14 @@ pub fn font_family_picker(app: &mut DesignApp, ui: &mut egui::Ui, current: &str,
|ui| {
ui.set_min_width(300.0);
ui.horizontal(|ui| {
ui.add(egui::TextEdit::singleline(&mut query).hint_text("Search fonts").desired_width(200.0));
ui.toggle_value(&mut only_favs, "Favorites").on_hover_text("Show Favorites Only");
ui.add(
egui::TextEdit::singleline(&mut query)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Search fonts")))
.desired_width(200.0),
);
ui.toggle_value(&mut only_favs, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Favorites"))).on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Show Favorites Only"));
});
});
let q = query.to_lowercase();
let ppp = ui.ctx().pixels_per_point();
@@ -285,10 +291,11 @@ pub fn font_style_picker(app: &mut DesignApp, ui: &mut egui::Ui, family: &str, c
pub fn para_style_picker(app: &mut DesignApp, ui: &mut egui::Ui, current: &str, overridden: bool, width: f32) {
let names: Vec<String> = app.session.active().map(|d| d.doc.styles.paragraph.iter().map(|p| p.name.clone()).collect()).unwrap_or_default();
let mut pick = None;
let label = if overridden { format!("{current}+") } else { current.to_string() };
egui::ComboBox::from_id_salt("para_style").selected_text(label).width(width).show_ui(ui, |ui| {
let shown = crate::i18n::style_name(&app.ui.language, current);
let label = if overridden { format!("{shown}+") } else { shown.to_string() };
egui::ComboBox::from_id_salt("para_style").selected_text(crate::rtl::widget(ui, label)).width(width).show_ui(ui, |ui| {
for n in names.iter().filter(|n| *n != designcraft_doc::NO_PARA_STYLE) {
if ui.selectable_label(n == current, n).clicked() {
if ui.selectable_label(n == current, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, n))).clicked() {
pick = Some(n.clone());
}
}
+16 -5
View File
@@ -13,8 +13,19 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let has_text = app.session.active().is_some_and(|d| d.selection.text.is_some());
let id = egui::Id::new("new_note_text");
let mut draft: String = ui.data(|d| d.get_temp(id)).unwrap_or_default();
ui.add(egui::TextEdit::multiline(&mut draft).hint_text("Note text").desired_rows(2).desired_width(f32::INFINITY));
if ui.add_enabled(has_text && !draft.trim().is_empty(), egui::Button::new("New Note at Cursor")).clicked() {
ui.add(
egui::TextEdit::multiline(&mut draft)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Note text")))
.desired_rows(2)
.desired_width(f32::INFINITY),
);
if ui
.add_enabled(
has_text && !draft.trim().is_empty(),
egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New Note at Cursor"))),
)
.clicked()
{
match app.run("note.new", json!({"text": draft.trim()})) {
Ok(_) => draft.clear(),
Err(e) => app.status(format!("Notes: {e}")),
@@ -37,13 +48,13 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
}
ui.label(n["text"].as_str().unwrap_or(""));
ui.horizontal(|ui| {
if ui.small_button("Go To").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Go To"))).clicked() {
let _ = app.run("text.select", json!({"story": story, "anchor": at, "focus": at + 3}));
}
if ui.small_button("Convert to Text").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Convert to Text"))).clicked() {
let _ = app.run("note.convertToText", json!({"story": story, "id": nid}));
}
if ui.small_button("Delete").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Delete"))).clicked() {
let _ = app.run("note.delete", json!({"story": story, "id": nid}));
}
});
+16 -5
View File
@@ -186,12 +186,21 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.add_space(6.0);
ui.spacing_mut().item_spacing.y = 4.0;
ui.horizontal(|ui| {
ui.label(egui::RichText::new(format!("{} Pages in {} Spreads", doc.page_count(), doc.spreads.len())).size(11.0).color(t.text_dim));
crate::rtl::label(
ui,
egui::RichText::new(
crate::i18n::tr(&app.ui.language, "{pages} Pages in {spreads} Spreads")
.replace("{pages}", &doc.page_count().to_string())
.replace("{spreads}", &doc.spreads.len().to_string()),
)
.size(11.0)
.color(t.text_dim),
);
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if icons::button(ui, "trash", 20.0, false, "Delete Page").clicked() {
if icons::button(ui, "trash", 20.0, false, crate::i18n::tr(&app.ui.language, "Delete Page")).clicked() {
let _ = app.run("layout.pages.delete", json!({"pages": [cur]}));
}
if icons::button(ui, "plus", 20.0, false, "Insert Page").clicked() {
if icons::button(ui, "plus", 20.0, false, crate::i18n::tr(&app.ui.language, "Insert Page")).clicked() {
let _ = app.run("layout.pages.insert", json!({"after": cur, "count": 1}));
}
});
@@ -223,7 +232,9 @@ fn page_menu(app: &mut DesignApp, ui: &mut egui::Ui, doc: &designcraft_doc::Docu
if si > 0
&& ui
.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Add to Previous Spread")))
.on_hover_text("Spreads of up to 10 pages")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Spreads of up to 10 pages"));
})
.clicked()
{
let _ = app.run("layout.pages.toSpread", json!({"page": abs + 1, "spread": si - 1}));
@@ -240,7 +251,7 @@ fn page_menu(app: &mut DesignApp, ui: &mut egui::Ui, doc: &designcraft_doc::Docu
("layout.removeOverrides", "Remove All Local Overrides"),
("layout.detachAll", "Detach All Objects from Parent"),
] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run(id, json!({"page": abs}));
ui.close();
}
+289 -173
View File
@@ -137,7 +137,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
("fit-frame", "fitFrameToContent", "Fit Frame to Content"),
("fit-center", "centerContent", "Center Content"),
] {
if icon_toggle(ui, icon, false, tip).clicked() {
if icon_toggle(ui, icon, false, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("object.fit", json!({"mode": mode}));
}
}
@@ -197,10 +197,10 @@ fn document_sections(app: &mut DesignApp, ui: &mut Ui) {
let mut orient = None;
place(ui, sub(b, 126.0, 0.0, 52.0, FIELD_H), |ui| {
ui.spacing_mut().item_spacing.x = 4.0;
if icon_toggle(ui, "orient-portrait", !landscape, "Portrait").clicked() && landscape {
if icon_toggle(ui, "orient-portrait", !landscape, crate::i18n::tr(&app.ui.language, "Portrait")).clicked() && landscape {
orient = Some(());
}
if icon_toggle(ui, "orient-landscape", landscape, "Landscape").clicked() && !landscape {
if icon_toggle(ui, "orient-landscape", landscape, crate::i18n::tr(&app.ui.language, "Landscape")).clicked() && !landscape {
orient = Some(());
}
});
@@ -258,7 +258,9 @@ fn document_sections(app: &mut DesignApp, ui: &mut Ui) {
for (k, (icon, tip, v, x, y)) in fields.into_iter().enumerate() {
let ir = sub(b, x, y + 2.0, 17.0, 17.0);
icons::paint(ui.painter(), ir, icon, t.icon);
ui.interact(ir, ui.id().with(("micon", k)), Sense::hover()).on_hover_text(tip);
ui.interact(ir, ui.id().with(("micon", k)), Sense::hover()).on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, tip));
});
let nv = place(ui, sub(b, x + 20.0, y, 73.0, FIELD_H), |ui| {
NumField::measure(tip, Some(v), u).width(73.0).spinner().range(0.0, 8000.0).show(ui)
});
@@ -326,13 +328,13 @@ fn document_sections(app: &mut DesignApp, ui: &mut Ui) {
let (mut rulers, mut bl, mut dg) = (app.ui.rulers, app.ui.baseline_grid, app.ui.document_grid);
section_row(ui, crate::i18n::tr(&app.ui.language, "Rulers & Grids"), |ui| {
ui.spacing_mut().item_spacing.x = 14.0;
if icon_toggle(ui, "grid-document", dg, "Show Document Grid").clicked() {
if icon_toggle(ui, "grid-document", dg, crate::i18n::tr(&app.ui.language, "Show Document Grid")).clicked() {
dg = !dg;
}
if icon_toggle(ui, "grid-baseline", bl, "Show Baseline Grid").clicked() {
if icon_toggle(ui, "grid-baseline", bl, crate::i18n::tr(&app.ui.language, "Show Baseline Grid")).clicked() {
bl = !bl;
}
if icon_toggle(ui, "rulers", rulers, "Show Rulers").clicked() {
if icon_toggle(ui, "rulers", rulers, crate::i18n::tr(&app.ui.language, "Show Rulers")).clicked() {
rulers = !rulers;
}
});
@@ -347,13 +349,13 @@ fn document_sections(app: &mut DesignApp, ui: &mut Ui) {
let (mut guides, mut locked, mut smart) = (app.ui.guides, app.ui.guides_locked, app.ui.smart_guides);
section_row(ui, crate::i18n::tr(&app.ui.language, "Guides"), |ui| {
ui.spacing_mut().item_spacing.x = 14.0;
if icon_toggle(ui, "guides-smart", smart, "Smart Guides").clicked() {
if icon_toggle(ui, "guides-smart", smart, crate::i18n::tr(&app.ui.language, "Smart Guides")).clicked() {
smart = !smart;
}
if icon_toggle(ui, "guides-lock", locked, "Lock Guides").clicked() {
if icon_toggle(ui, "guides-lock", locked, crate::i18n::tr(&app.ui.language, "Lock Guides")).clicked() {
locked = !locked;
}
if icon_toggle(ui, "guides-show", guides, "Show Guides").clicked() {
if icon_toggle(ui, "guides-show", guides, crate::i18n::tr(&app.ui.language, "Show Guides")).clicked() {
guides = !guides;
}
});
@@ -457,7 +459,7 @@ fn transform_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
set.insert("ref".into(), json!(rp));
let _ = app.run("transform.set", Value::Object(set));
}
if more_options(ui).clicked() {
if more_options(ui, &app.ui.language).clicked() {
app.ui.open_panel = Some("info".into());
}
}
@@ -477,7 +479,7 @@ fn stroke_type_dropdown(app: &mut DesignApp, ui: &mut Ui, cur: &str, w: f32) {
egui::Popup::menu(&resp).show(|ui| {
ui.set_min_width(140.0);
for (label, kind) in TYPES {
if ui.selectable_label(*kind == cur, *label).clicked() {
if ui.selectable_label(*kind == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let ty = if *kind == "dashed" { json!({"kind": "dashed", "pattern": [12.0, 4.0]}) } else { json!({"kind": kind}) };
let _ = app.run("object.stroke", json!({"type": ty}));
ui.close();
@@ -573,7 +575,14 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
swatch_menu(app, &resp, &doc, &i.fill, |app, n| {
let _ = app.run("object.fill", json!({"swatch": n}));
});
ui.painter().text(b.min + vec2(34.0, FIELD_H / 2.0), egui::Align2::LEFT_CENTER, "Fill", egui::FontId::proportional(11.5), t.text);
crate::rtl::paint(
ui.painter(),
b.min + vec2(34.0, FIELD_H / 2.0),
egui::Align2::LEFT_CENTER,
crate::i18n::tr(&app.ui.language, "Fill"),
egui::FontId::proportional(11.5),
t.text,
);
ui.add_space(4.0);
// Stroke.
let b = block(ui, FIELD_H);
@@ -585,7 +594,7 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
swatch_menu(app, &resp, &doc, &i.stroke, |app, n| {
let _ = app.run("object.stroke", json!({"swatch": n}));
});
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, "Stroke", 56.0)).clicked() {
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, crate::i18n::tr(&app.ui.language, "Stroke"), 56.0)).clicked() {
app.ui.open_panel = Some("stroke".into());
}
const WEIGHTS: &[f64] = &[0.0, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 20.0, 30.0, 40.0, 50.0, 100.0];
@@ -600,7 +609,7 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
// Corner.
let b = block(ui, FIELD_H);
icons::paint(ui.painter(), sub(b, 3.0, 1.0, 19.0, 19.0), "corner", t.accent);
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, "Corner", 56.0)).clicked() {
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, crate::i18n::tr(&app.ui.language, "Corner"), 56.0)).clicked() {
app.ui.dialog = Some(crate::dialogs::Dialog::new("frameSize", json!({})));
}
let (shape, size) = i.corner;
@@ -615,7 +624,7 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
egui::Popup::menu(&resp).show(|ui| {
ui.set_min_width(140.0);
for s in CornerShape::ALL {
if ui.selectable_label(s == shape, s.label()).clicked() {
if ui.selectable_label(s == shape, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, s.label()))).clicked() {
let _ = app.run("object.cornerOptions", json!({"shape": s, "size": if size > 0.0 { size } else { 12.0 }}));
ui.close();
}
@@ -626,7 +635,7 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
// Opacity.
let b = block(ui, FIELD_H);
icons::paint(ui.painter(), sub(b, 3.0, 1.0, 19.0, 19.0), "opacity", t.icon);
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, "Opacity", 56.0)).clicked() {
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, crate::i18n::tr(&app.ui.language, "Opacity"), 56.0)).clicked() {
app.ui.open_panel = Some("effects".into());
}
if let Some(v) = place(ui, sub(b, 96.0, 0.0, 51.0, FIELD_H), |ui| {
@@ -644,10 +653,10 @@ fn appearance_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
ui.add_space(6.0);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 10.0;
if icon_toggle(ui, "frame-dashed", false, "Frame Fitting").clicked() {
if icon_toggle(ui, "frame-dashed", false, crate::i18n::tr(&app.ui.language, "Frame Fitting")).clicked() {
app.ui.open_panel = Some("textWrap".into());
}
if icon_toggle(ui, "fx", i.shadow, "Effects: Drop Shadow").clicked() {
if icon_toggle(ui, "fx", i.shadow, crate::i18n::tr(&app.ui.language, "Effects: Drop Shadow")).clicked() {
let _ = app.run("object.dropShadow", json!({"on": !i.shadow}));
}
});
@@ -692,7 +701,7 @@ fn align_row(app: &mut DesignApp, ui: &mut Ui) {
("Align to Page", "page"),
("Align to Spread", "spread"),
] {
if ui.selectable_label(app.ui.align_to == id, label).clicked() {
if ui.selectable_label(app.ui.align_to == id, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
app.ui.align_to = id.into();
ui.close();
}
@@ -708,7 +717,7 @@ fn align_row(app: &mut DesignApp, ui: &mut Ui) {
("vcenter", "objalign-vcenter", "Align vertical centers"),
("bottom", "objalign-bottom", "Align bottom edges"),
] {
if icon_toggle(ui, icon, false, tip).clicked() {
if icon_toggle(ui, icon, false, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("object.align", json!({"edge": edge, "to": to}));
}
}
@@ -725,7 +734,7 @@ fn wrap_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
("wrap-jump", "jumpObject", "Jump Object"),
("wrap-next", "jumpToNextColumn", "Jump to Next Column"),
] {
if icon_toggle(ui, icon, i.wrap == mode, tip).clicked() {
if icon_toggle(ui, icon, i.wrap == mode, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("object.textWrap", json!({"mode": mode}));
}
}
@@ -737,7 +746,7 @@ fn wrap_section(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
let _ = app.run("object.textWrap", json!({"mode": i.wrap, "invert": inv}));
}
});
if more_options(ui).clicked() {
if more_options(ui, &app.ui.language).clicked() {
app.ui.open_panel = Some("textWrap".into());
}
}
@@ -750,7 +759,7 @@ fn arrange_menu(app: &mut DesignApp, resp: &egui::Response) {
("Send Backward", "object.sendBackward"),
("Send to Back", "object.sendToBack"),
] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run(cmd, json!({}));
ui.close();
}
@@ -771,7 +780,7 @@ fn convert_shape_menu(app: &mut DesignApp, resp: &egui::Response) {
("Line", "line"),
("Orthogonal Line", "orthogonalLine"),
] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
if let Err(e) = app.run("object.convertShape", json!({"to": to})) {
app.status(format!("Convert Shape: {e}"));
}
@@ -806,7 +815,7 @@ fn quick_actions_object(app: &mut DesignApp, ui: &mut Ui, i: &SelInfo) {
ui.spacing_mut().item_spacing.x = 10.0;
let r = outline_button(ui, crate::i18n::tr(&app.ui.language, "Arrange"), hw);
arrange_menu(app, &r);
if outline_button(ui, if i.count > 1 { "Group" } else { "Lock" }, hw).clicked() {
if outline_button(ui, crate::i18n::tr(&app.ui.language, if i.count > 1 { "Group" } else { "Lock" }), hw).clicked() {
let _ = app.run(if i.count > 1 { "object.group" } else { "object.lock" }, json!({}));
}
});
@@ -829,7 +838,12 @@ fn text_style_section(app: &mut DesignApp, ui: &mut Ui) {
let Some(a) = text_attrs(app) else { return };
let tab = app.ui.text_style_tab;
let fw = full_width(ui);
if let Some(k) = widgets::segmented(ui, &["Paragraph Styles", "Character Styles"], tab as usize, fw) {
if let Some(k) = widgets::segmented(
ui,
&[crate::i18n::tr(&app.ui.language, "Paragraph Styles"), crate::i18n::tr(&app.ui.language, "Character Styles")],
tab as usize,
fw,
) {
app.ui.text_style_tab = k as u8;
}
ui.add_space(5.0);
@@ -845,13 +859,15 @@ fn text_style_section(app: &mut DesignApp, ui: &mut Ui) {
}
};
let ov = if tab == 0 { a["paraOverrides"].as_u64().unwrap_or(0) + a["charOverrides"].as_u64().unwrap_or(0) } else { 0 };
let label = if ov > 0 { format!("{cur}+") } else { cur.clone() };
let shown = crate::i18n::style_name(&app.ui.language, &cur);
let label = if ov > 0 { format!("{shown}+") } else { shown.to_string() };
let resp = widgets::dropdown_h(ui, "", fw, 34.0);
// "Ag" swatch + style name.
let ag = Rect::from_min_size(resp.rect.min + vec2(6.0, 6.0), vec2(22.0, 22.0));
ui.painter().rect_filled(ag, 1.0, egui::Color32::WHITE);
ui.painter().text(ag.center(), egui::Align2::CENTER_CENTER, "Ag", egui::FontId::new(13.0, egui::FontFamily::Proportional), egui::Color32::BLACK);
ui.painter().with_clip_rect(resp.rect.shrink2(vec2(20.0, 0.0))).text(
crate::rtl::paint(
&ui.painter().with_clip_rect(resp.rect.shrink2(vec2(20.0, 0.0))),
pos2(ag.max.x + 8.0, resp.rect.center().y),
egui::Align2::LEFT_CENTER,
&label,
@@ -863,7 +879,7 @@ fn text_style_section(app: &mut DesignApp, ui: &mut Ui) {
ui.set_min_width(fw);
egui::ScrollArea::vertical().max_height(320.0).show(ui, |ui| {
for n in &names {
if ui.selectable_label(*n == cur, n).clicked() {
if ui.selectable_label(*n == cur, crate::rtl::widget(ui, crate::i18n::style_name(&app.ui.language, n))).clicked() {
pick = Some(n.clone());
ui.close();
}
@@ -880,20 +896,32 @@ fn text_style_section(app: &mut DesignApp, ui: &mut Ui) {
ui.add_space(4.0);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 8.0;
if icon_toggle(ui, "pilcrow-menu", false, if tab == 0 { "Paragraph Styles panel" } else { "Character Styles panel" }).clicked() {
if icon_toggle(
ui,
"pilcrow-menu",
false,
crate::i18n::tr(&app.ui.language, if tab == 0 { "Paragraph Styles panel" } else { "Character Styles panel" }),
)
.clicked()
{
app.ui.open_panel = Some(if tab == 0 { "paragraphStyles" } else { "characterStyles" }.into());
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.spacing_mut().item_spacing.x = 12.0;
ui.add_space(4.0);
if icon_toggle(ui, "style-new", false, "Create new style").clicked() {
if icon_toggle(ui, "style-new", false, crate::i18n::tr(&app.ui.language, crate::i18n::tr(&app.ui.language, "Create new style"))).clicked()
{
let cmd = if tab == 0 { "style.paragraph.create" } else { "style.character.create" };
let _ = app.run(cmd, json!({}));
}
if icon_toggle(ui, "style-clear", false, "Clear overrides").clicked() && tab == 0 && !cur.is_empty() {
if icon_toggle(ui, "style-clear", false, crate::i18n::tr(&app.ui.language, crate::i18n::tr(&app.ui.language, "Clear overrides")))
.clicked()
&& tab == 0
&& !cur.is_empty()
{
let _ = app.run("style.paragraph.apply", json!({"name": cur, "clearOverrides": true}));
}
if icon_toggle(ui, "style-load", false, "Styles panel").clicked() {
if icon_toggle(ui, "style-load", false, crate::i18n::tr(&app.ui.language, "Styles panel")).clicked() {
app.ui.open_panel = Some(if tab == 0 { "paragraphStyles" } else { "characterStyles" }.into());
}
});
@@ -926,9 +954,16 @@ fn text_appearance_section(app: &mut DesignApp, ui: &mut Ui) {
let _ = app.run("type.char", json!({"attrs": {key: n}}));
});
if k == 0 {
ui.painter().text(b.min + vec2(34.0, FIELD_H / 2.0), egui::Align2::LEFT_CENTER, label, egui::FontId::proportional(11.5), t.text);
crate::rtl::paint(
ui.painter(),
b.min + vec2(34.0, FIELD_H / 2.0),
egui::Align2::LEFT_CENTER,
crate::i18n::tr(&app.ui.language, label),
egui::FontId::proportional(11.5),
t.text,
);
} else {
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, label, 56.0)).clicked() {
if place(ui, sub(b, 34.0, 0.0, 56.0, FIELD_H), |ui| link_label(ui, crate::i18n::tr(&app.ui.language, label), 56.0)).clicked() {
app.ui.open_panel = Some("stroke".into());
}
const WEIGHTS: &[f64] = &[0.0, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0];
@@ -1019,10 +1054,10 @@ fn character_section(app: &mut DesignApp, ui: &mut Ui) {
_ => "Metrics".to_string(),
};
place(ui, sub(b, 24.0, 26.0, half - 24.0, FIELD_H), |ui| {
let resp = widgets::dropdown(ui, &kern_label, half - 24.0);
let resp = widgets::dropdown(ui, crate::i18n::tr(&app.ui.language, &kern_label), half - 24.0);
egui::Popup::menu(&resp).show(|ui| {
for (label, v) in [("Metrics", json!({"kind": "metrics"})), ("Optical", json!({"kind": "optical"})), ("0", json!({"kind": "none"}))] {
if ui.selectable_label(kern_label == label, label).clicked() {
if ui.selectable_label(kern_label == label, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("type.char", json!({"attrs": {"kerning": v}}));
ui.close();
}
@@ -1036,7 +1071,7 @@ fn character_section(app: &mut DesignApp, ui: &mut Ui) {
}) {
let _ = app.run("type.char", json!({"attrs": {"tracking": v}}));
}
if more_options(ui).clicked() {
if more_options(ui, &app.ui.language).clicked() {
app.ui.open_panel = Some("character".into());
}
}
@@ -1068,7 +1103,7 @@ fn paragraph_section(app: &mut DesignApp, ui: &mut Ui) {
}
}
});
if more_options(ui).clicked() {
if more_options(ui, &app.ui.language).clicked() {
app.ui.open_panel = Some("paragraph".into());
}
}
@@ -1080,7 +1115,7 @@ fn bullets_section(app: &mut DesignApp, ui: &mut Ui) {
ui.spacing_mut().item_spacing.x = 10.0;
for (icon, kind, tip) in [("bullets", "bullets", "Bulleted List"), ("numbering", "numbers", "Numbered List")] {
let on = lt == kind;
if icon_toggle(ui, icon, on, tip).clicked() {
if icon_toggle(ui, icon, on, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("type.para", json!({"attrs": {"listType": if on { "none" } else { kind }}}));
}
}
@@ -1101,7 +1136,7 @@ fn quick_actions_text(app: &mut DesignApp, ui: &mut Ui) {
let r = outline_button(ui, crate::i18n::tr(&app.ui.language, "Change Case"), hw);
egui::Popup::menu(&r).show(|ui| {
for (label, v) in [("UPPERCASE", "allCaps"), ("Small Caps", "smallCaps"), ("Normal", "normal")] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("type.char", json!({"attrs": {"capitalization": v}}));
ui.close();
}
@@ -1184,82 +1219,112 @@ pub fn character_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let c = a["chars"].clone();
let fam = c["fontFamily"].as_str().unwrap_or("").to_string();
let sty = c["fontStyle"].as_str().unwrap_or("").to_string();
ui.label(egui::RichText::new("Character").strong());
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Character")).strong());
super::font_family_picker(app, ui, &fam, 220.0);
super::font_style_picker(app, ui, &fam, &sty, 220.0);
variable_font_axes(app, ui, &fam, &sty);
// Language: hyphenation, spelling and typographer's quotes follow it.
let lang = c["language"].as_str().unwrap_or("English: USA").to_string();
ui.horizontal(|ui| {
caption(ui, "Language");
egui::ComboBox::from_id_salt("char_language").selected_text(&lang).width(170.0).show_ui(ui, |ui| {
for l in LANGUAGES {
if ui.selectable_label(*l == lang, *l).clicked() {
let _ = app.run("type.char", json!({"attrs": {"language": l}}));
caption(ui, crate::i18n::tr(&app.ui.language, "Language"));
egui::ComboBox::from_id_salt("char_language")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, &lang)))
.width(170.0)
.show_ui(ui, |ui| {
for l in LANGUAGES {
if ui.selectable_label(*l == lang, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
let _ = app.run("type.char", json!({"attrs": {"language": l}}));
}
}
}
});
});
});
// Digits (World-Ready): shown for right-to-left paragraphs or once set.
let digits = c["digits"].as_str().unwrap_or("default").to_string();
if digits != "default" || a["para"]["direction"].as_str() == Some("rightToLeft") {
ui.horizontal(|ui| {
caption(ui, "Digits");
caption(ui, crate::i18n::tr(&app.ui.language, "Digits"));
let opts = [("default", "Default"), ("arabic", "Arabic"), ("hindi", "Hindi"), ("farsi", "Farsi"), ("native", "Native")];
let cur = opts.iter().find(|o| o.0 == digits).map_or("Default", |o| o.1);
egui::ComboBox::from_id_salt("char_digits").selected_text(cur).width(120.0).show_ui(ui, |ui| {
for (k, l) in opts {
if ui.selectable_label(k == digits, l).clicked() {
let _ = app.run("type.char", json!({"attrs": {"digits": k}}));
egui::ComboBox::from_id_salt("char_digits")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, cur)))
.width(120.0)
.show_ui(ui, |ui| {
for (k, l) in opts {
if ui.selectable_label(k == digits, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, l))).clicked() {
let _ = app.run("type.char", json!({"attrs": {"digits": k}}));
}
}
}
});
});
});
}
egui::Grid::new("chargrid").num_columns(4).spacing(vec2(6.0, 4.0)).show(ui, |ui| {
caption(ui, "Size");
caption(ui, crate::i18n::tr(&app.ui.language, "Size"));
if let Some(v) = number(ui, "pcs", c["size"].as_f64(), " pt", 60.0, 2) {
let _ = app.run("type.char", json!({"attrs": {"size": v}}));
}
caption(ui, "Leading");
caption(ui, crate::i18n::tr(&app.ui.language, "Leading"));
let auto = c["leading"]["kind"].as_str() != Some("points");
let lv = if auto { c["size"].as_f64().map(|s| s * a["para"]["autoLeading"].as_f64().unwrap_or(1.2)) } else { c["leading"]["value"].as_f64() };
if let Some(v) = number(ui, "pcl", lv, if auto { " (auto)" } else { " pt" }, 70.0, 2) {
if let Some(v) = number(ui, "pcl", lv, if auto { crate::i18n::tr(&app.ui.language, " (auto)") } else { " pt" }, 70.0, 2) {
let _ = app.run("type.char", json!({"attrs": {"leading": {"kind": "points", "value": v}}}));
}
ui.end_row();
caption(ui, "Tracking");
caption(ui, crate::i18n::tr(&app.ui.language, "Tracking"));
if let Some(v) = number(ui, "pct", c["tracking"].as_f64(), "", 60.0, 0) {
let _ = app.run("type.char", json!({"attrs": {"tracking": v}}));
}
caption(ui, "Baseline");
caption(ui, crate::i18n::tr(&app.ui.language, "Baseline"));
if let Some(v) = number(ui, "pcb", c["baselineShift"].as_f64(), " pt", 70.0, 2) {
let _ = app.run("type.char", json!({"attrs": {"baselineShift": v}}));
}
ui.end_row();
});
ui.horizontal(|ui| {
caption(ui, "Color");
caption(ui, crate::i18n::tr(&app.ui.language, "Color"));
super::swatch_picker(app, ui, "tfill", c["fill"].as_str().map(str::to_string), |app, n| {
let _ = app.run("type.char", json!({"attrs": {"fill": n}}));
});
ui.label(c["fill"].as_str().unwrap_or(""));
crate::rtl::label(ui, c["fill"].as_str().unwrap_or(""));
});
ui.horizontal(|ui| {
let caps = c["capitalization"].as_str() == Some("allCaps");
if ui.selectable_label(caps, "TT").on_hover_text("All Caps").clicked() {
if ui
.selectable_label(caps, "TT")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "All Caps"));
})
.clicked()
{
let _ = app.run("type.char", json!({"attrs": {"capitalization": if caps { "normal" } else { "allCaps" }}}));
}
let ul = c["underline"].as_bool().unwrap_or(false);
if ui.selectable_label(ul, "U").on_hover_text("Underline").clicked() {
if ui
.selectable_label(ul, "U")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Underline"));
})
.clicked()
{
let _ = app.run("type.char", json!({"attrs": {"underline": !ul}}));
}
let st = c["strikethrough"].as_bool().unwrap_or(false);
if ui.selectable_label(st, "S").on_hover_text("Strikethrough").clicked() {
if ui
.selectable_label(st, "S")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Strikethrough"));
})
.clicked()
{
let _ = app.run("type.char", json!({"attrs": {"strikethrough": !st}}));
}
let sup = c["position"].as_str() == Some("superscript");
if ui.selectable_label(sup, "T¹").on_hover_text("Superscript").clicked() {
if ui
.selectable_label(sup, "T¹")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Superscript"));
})
.clicked()
{
let _ = app.run("type.char", json!({"attrs": {"position": if sup { "normal" } else { "superscript" }}}));
}
let features: Vec<String> =
@@ -1271,16 +1336,16 @@ pub fn character_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
if !c[key].as_bool().unwrap_or(false) {
continue;
}
egui::CollapsingHeader::new(title).id_salt(key).show(ui, |ui| {
egui::CollapsingHeader::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, title))).id_salt(key).show(ui, |ui| {
let size = c["size"].as_f64().unwrap_or(12.0);
let auto_off = if key == "underline" { size * 0.12 } else { size * 0.3 };
egui::Grid::new((key, "opts")).num_columns(4).spacing(vec2(6.0, 4.0)).show(ui, |ui| {
caption(ui, "Weight");
caption(ui, crate::i18n::tr(&app.ui.language, "Weight"));
let w = c[format!("{key}Weight")].as_f64().unwrap_or(size / 14.0);
if let Some(v) = number(ui, &format!("{key}w"), Some(w), " pt", 60.0, 2) {
let _ = app.run("type.char", json!({"attrs": {format!("{key}Weight"): v.max(0.0)}}));
}
caption(ui, "Offset");
caption(ui, crate::i18n::tr(&app.ui.language, "Offset"));
let o = c[format!("{key}Offset")].as_f64().unwrap_or(auto_off);
if let Some(v) = number(ui, &format!("{key}o"), Some(o), " pt", 60.0, 2) {
let _ = app.run("type.char", json!({"attrs": {format!("{key}Offset"): v}}));
@@ -1288,12 +1353,12 @@ pub fn character_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.end_row();
});
ui.horizontal(|ui| {
caption(ui, "Color");
caption(ui, crate::i18n::tr(&app.ui.language, "Color"));
let cur = c[format!("{key}Color")].as_str().filter(|s| !s.is_empty()).map(str::to_string);
super::swatch_picker(app, ui, &format!("{key}c"), cur.clone(), |app, n| {
let _ = app.run("type.char", json!({"attrs": {format!("{key}Color"): n}}));
});
ui.label(cur.unwrap_or_else(|| "(Text Color)".into()));
crate::rtl::label(ui, cur.unwrap_or_else(|| crate::i18n::tr(&app.ui.language, "(Text Color)").into()));
});
});
}
@@ -1305,7 +1370,7 @@ pub fn open_type_menu(app: &mut DesignApp, ui: &mut egui::Ui, features: &[String
ui.set_min_width(220.0);
for (tag, label, _) in otf::TOGGLES {
let mut on = otf::is_on(features, tag);
if ui.checkbox(&mut on, *label).clicked() {
if ui.checkbox(&mut on, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("type.openType", json!({"feature": tag, "on": on}));
ui.close();
}
@@ -1323,7 +1388,7 @@ pub fn open_type_menu(app: &mut DesignApp, ui: &mut egui::Ui, features: &[String
let mask = otf::stylistic_sets(features);
for n in 1..=20u32 {
let mut on = mask & (1 << (n - 1)) != 0;
if ui.checkbox(&mut on, format!("Set {n}")).clicked() {
if ui.checkbox(&mut on, crate::rtl::widget(ui, format!("{} {n}", crate::i18n::tr(&app.ui.language, "Set")))).clicked() {
let sets: Vec<u32> = (1..=20).filter(|k| if *k == n { on } else { mask & (1 << (k - 1)) != 0 }).collect();
let _ = app.run("type.openType", json!({"stylisticSets": sets}));
}
@@ -1338,7 +1403,7 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
};
let u = units(app);
let p = a["para"].clone();
ui.label(egui::RichText::new("Paragraph").strong());
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Paragraph")).strong());
let cur: Align = serde_json::from_value(p["align"].clone()).unwrap_or_default();
ui.horizontal(|ui| {
for (al, icon) in [
@@ -1356,9 +1421,9 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
// Paragraph Direction (World-Ready): switching also mirrors left/right alignment.
let rtl = p["direction"] == "rightToLeft";
ui.horizontal(|ui| {
caption(ui, "Direction");
caption(ui, crate::i18n::tr(&app.ui.language, "Direction"));
for (on, label, dir) in [(!rtl, "Left to Right", "leftToRight"), (rtl, "Right to Left", "rightToLeft")] {
if ui.selectable_label(on, label).clicked() && !on {
if ui.selectable_label(on, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() && !on {
let align = match cur {
Align::Left => Align::Right,
Align::Right => Align::Left,
@@ -1377,11 +1442,11 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
.into_iter()
.enumerate()
{
caption(ui, la);
caption(ui, crate::i18n::tr(&app.ui.language, la));
if let Some(v) = measure(ui, &format!("pp{row}a"), p[ka].as_f64(), u, 64.0) {
let _ = app.run("type.para", json!({"attrs": {ka: v}}));
}
caption(ui, lb);
caption(ui, crate::i18n::tr(&app.ui.language, lb));
if let Some(v) = measure(ui, &format!("pp{row}b"), p[kb].as_f64(), u, 64.0) {
let _ = app.run("type.para", json!({"attrs": {kb: v}}));
}
@@ -1399,7 +1464,7 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
}
});
ui.horizontal(|ui| {
caption(ui, "Composer");
caption(ui, crate::i18n::tr(&app.ui.language, "Composer"));
let single = p["composer"].as_str() == Some("singleLine");
if ui.selectable_label(!single, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Paragraph"))).clicked() {
let _ = app.run("type.para", json!({"attrs": {"composer": "paragraph"}}));
@@ -1414,7 +1479,7 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
for (on_key, color_key, label) in [("shadingOn", "shadingColor", "Shading"), ("borderOn", "borderColor", "Border")] {
ui.horizontal(|ui| {
let mut on = p[on_key].as_bool().unwrap_or(false);
if ui.checkbox(&mut on, label).changed() {
if ui.checkbox(&mut on, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).changed() {
let _ = app.run("type.para", json!({ on_key: on }));
}
if on {
@@ -1439,7 +1504,7 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
}
if p["shadingOn"].as_bool().unwrap_or(false) || p["borderOn"].as_bool().unwrap_or(false) {
ui.horizontal(|ui| {
caption(ui, "Offsets");
caption(ui, crate::i18n::tr(&app.ui.language, "Offsets"));
let o = p["borderOffsets"][0].as_f64().or(p["shadingOffsets"][0].as_f64());
if let Some(v) = number(ui, "pbo", o, " pt", 50.0, 2) {
let o = [v; 4];
@@ -1450,7 +1515,7 @@ pub fn paragraph_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let ps = a["paragraphStyle"].as_str().unwrap_or("").to_string();
let ov = a["paraOverrides"].as_u64().unwrap_or(0) + a["charOverrides"].as_u64().unwrap_or(0);
ui.horizontal(|ui| {
caption(ui, "Style");
caption(ui, crate::i18n::tr(&app.ui.language, "Style"));
super::para_style_picker(app, ui, &ps, ov > 0, 190.0);
});
}
@@ -1466,102 +1531,111 @@ pub fn stroke_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
};
let t = Tokens::get(ui.ctx());
egui::Grid::new("stroke_panel").num_columns(2).spacing(vec2(8.0, 6.0)).show(ui, |ui| {
caption(ui, "Weight:");
caption(ui, crate::i18n::tr(&app.ui.language, "Weight:"));
if let Some(v) = number(ui, "sw", Some(i.stroke_weight), " pt", 60.0, 3) {
let _ = app.run("object.stroke", json!({"weight": v}));
}
ui.end_row();
caption(ui, "Cap:");
caption(ui, crate::i18n::tr(&app.ui.language, "Cap:"));
ui.horizontal(|ui| {
for (label, v, cur) in [("Butt", "butt", Cap::Butt), ("Round", "round", Cap::Round), ("Projecting", "projecting", Cap::Projecting)] {
if ui.selectable_label(st.cap == cur, label).clicked() {
if ui.selectable_label(st.cap == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.stroke", json!({"cap": v}));
}
}
});
ui.end_row();
caption(ui, "Join:");
caption(ui, crate::i18n::tr(&app.ui.language, "Join:"));
ui.horizontal(|ui| {
for (label, v, cur) in [("Miter", "miter", Join::Miter), ("Round", "round", Join::Round), ("Bevel", "bevel", Join::Bevel)] {
if ui.selectable_label(st.join == cur, label).clicked() {
if ui.selectable_label(st.join == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.stroke", json!({"join": v}));
}
}
});
ui.end_row();
caption(ui, "Miter Limit:");
caption(ui, crate::i18n::tr(&app.ui.language, "Miter Limit:"));
if let Some(v) = number(ui, "smiter", Some(st.miter_limit), " x", 60.0, 0) {
let _ = app.run("object.stroke", json!({"miterLimit": v}));
}
ui.end_row();
caption(ui, "Align Stroke:");
caption(ui, crate::i18n::tr(&app.ui.language, "Align Stroke:"));
ui.horizontal(|ui| {
for (label, a, cur) in
[("Center", "center", StrokeAlign::Center), ("Inside", "inside", StrokeAlign::Inside), ("Outside", "outside", StrokeAlign::Outside)]
{
if ui.selectable_label(st.align == cur, label).clicked() {
if ui.selectable_label(st.align == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.stroke", json!({"align": a}));
}
}
});
ui.end_row();
caption(ui, "Type:");
caption(ui, crate::i18n::tr(&app.ui.language, "Type:"));
let custom: Vec<String> = app.session.active().map(|d| d.doc.stroke_styles.iter().map(|x| x.name.clone()).collect()).unwrap_or_default();
let shown = match &st.kind {
StrokeType::Style { name } => name.clone(),
k => k.label().to_string(),
};
egui::ComboBox::from_id_salt("stype").selected_text(&shown).width(140.0).show_ui(ui, |ui| {
let built_in = [
"solid",
"dashed",
"dotted",
"thickThin",
"thinThick",
"thinThin",
"thickThick",
"thinThickThin",
"thickThinThick",
"wavy",
"hashed",
];
for k in built_in {
let ty = if k == "dashed" { json!({"kind": k, "pattern": [12.0, 4.0]}) } else { json!({"kind": k}) };
let label = serde_json::from_value::<StrokeType>(ty.clone()).map(|k| k.label()).unwrap_or("");
if ui.selectable_label(shown == label, label).clicked() {
let _ = app.run("object.stroke", json!({"type": ty}));
egui::ComboBox::from_id_salt("stype")
.selected_text(crate::rtl::widget(
ui,
if matches!(st.kind, StrokeType::Style { .. }) { &shown } else { crate::i18n::tr(&app.ui.language, &shown) },
))
.width(140.0)
.show_ui(ui, |ui| {
let built_in = [
"solid",
"dashed",
"dotted",
"thickThin",
"thinThick",
"thinThin",
"thickThick",
"thinThickThin",
"thickThinThick",
"wavy",
"hashed",
];
for k in built_in {
let ty = if k == "dashed" { json!({"kind": k, "pattern": [12.0, 4.0]}) } else { json!({"kind": k}) };
let label = serde_json::from_value::<StrokeType>(ty.clone()).map(|k| k.label()).unwrap_or("");
if ui.selectable_label(shown == label, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.stroke", json!({"type": ty}));
}
}
}
if !custom.is_empty() {
ui.separator();
}
for n in &custom {
if ui.selectable_label(shown == *n, n).clicked() {
let _ = app.run("object.stroke", json!({"type": {"kind": "style", "name": n}}));
if !custom.is_empty() {
ui.separator();
}
}
});
ui.end_row();
for (key, label, cur, flip) in [("start", "Start/End:", st.start, true), ("end", "", st.end, false)] {
caption(ui, label);
egui::ComboBox::from_id_salt(key).selected_text(cur.label()).width(140.0).show_ui(ui, |ui| {
for a in Arrowhead::ALL {
ui.horizontal(|ui| {
arrow_preview(ui, a, flip, t.text_strong);
if ui.selectable_label(a == cur, a.label()).clicked() {
let _ = app.run("object.stroke", json!({key: a}));
}
});
for n in &custom {
if ui.selectable_label(shown == *n, n).clicked() {
let _ = app.run("object.stroke", json!({"type": {"kind": "style", "name": n}}));
}
}
});
ui.end_row();
for (key, label, cur, flip) in [("start", "Start/End:", st.start, true), ("end", "", st.end, false)] {
caption(ui, crate::i18n::tr(&app.ui.language, label));
egui::ComboBox::from_id_salt(key)
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, cur.label())))
.width(140.0)
.show_ui(ui, |ui| {
for a in Arrowhead::ALL {
ui.horizontal(|ui| {
arrow_preview(ui, a, flip, t.text_strong);
if ui.selectable_label(a == cur, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, a.label()))).clicked() {
let _ = app.run("object.stroke", json!({key: a}));
}
});
}
});
ui.end_row();
}
caption(ui, "Gap Color:");
caption(ui, crate::i18n::tr(&app.ui.language, "Gap Color:"));
super::swatch_picker(app, ui, "sgap", Some(st.gap_swatch.clone()), |app, n| {
let _ = app.run("object.stroke", json!({"gapSwatch": n}));
});
ui.end_row();
caption(ui, "Color:");
caption(ui, crate::i18n::tr(&app.ui.language, "Color:"));
super::swatch_picker(app, ui, "spick", Some(i.stroke.clone()), |app, n| {
let _ = app.run("object.stroke", json!({"swatch": n}));
});
@@ -1621,14 +1695,14 @@ pub fn wrap_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
("wrap-next", "jumpToNextColumn", "Jump to Next Column"),
] {
let on = info.as_ref().is_some_and(|i| i.wrap == mode);
if icons::button(ui, icon, 26.0, on, tip).clicked() {
if icons::button(ui, icon, 26.0, on, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("object.textWrap", json!({"mode": mode}));
}
}
});
let u = units(app);
ui.horizontal(|ui| {
caption(ui, "Offset");
caption(ui, crate::i18n::tr(&app.ui.language, "Offset"));
if let Some(v) = measure(ui, "wo", Some(0.0), u, 60.0) {
let _ = app.run("object.textWrap", json!({"mode": info.as_ref().map(|i| i.wrap).unwrap_or("boundingBox"), "offset": v}));
}
@@ -1637,12 +1711,12 @@ pub fn wrap_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
/// Pathfinder panel: combine the selected shapes; make or release compound paths.
pub fn pathfinder_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.label(egui::RichText::new("Pathfinder").strong());
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Pathfinder")).strong());
ui.horizontal_wrapped(|ui| {
for (label, op) in
[("Add", "add"), ("Subtract", "subtract"), ("Intersect", "intersect"), ("Exclude Overlap", "exclude"), ("Minus Back", "minusBack")]
{
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.pathfinder", json!({"op": op}));
}
}
@@ -1678,7 +1752,9 @@ pub fn align_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
"vcenter" => "↕",
_ => "⤓",
})
.on_hover_text(tip)
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, tip));
})
.clicked()
{
let _ = app.run("object.align", json!({"edge": edge}));
@@ -1686,10 +1762,10 @@ pub fn align_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
}
});
ui.horizontal(|ui| {
if ui.small_button("Distribute ↔").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Distribute ↔"))).clicked() {
let _ = app.run("object.distribute", json!({"axis": "horizontal"}));
}
if ui.small_button("Distribute ↕").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Distribute ↕"))).clicked() {
let _ = app.run("object.distribute", json!({"axis": "vertical"}));
}
});
@@ -1701,17 +1777,20 @@ pub fn links_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
// Linked stories (Place and Link).
let stories = app.session.execute("story.links", &json!({})).ok().and_then(|v| v.as_array().cloned()).unwrap_or_default();
if !stories.is_empty() {
ui.label(egui::RichText::new("Linked Stories").strong());
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Linked Stories")).strong());
for l in &stories {
let out = l["outOfDate"].as_bool().unwrap_or(false);
ui.horizontal(|ui| {
ui.label(format!("Story {} ← story {}", l["story"], l["parent"]));
crate::rtl::label(ui, format!("Story {} ← story {}", l["story"], l["parent"]));
if out {
ui.label(crate::rtl::widget(
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "⚠ out of date")).color(egui::Color32::from_rgb(230, 160, 40)),
));
if ui.small_button("Update").clicked() {
crate::rtl::widget(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "⚠ out of date")).color(egui::Color32::from_rgb(230, 160, 40)),
),
);
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Update"))).clicked() {
let _ = app.run("story.updateLink", json!({"story": l["story"]}));
}
}
@@ -1741,7 +1820,7 @@ pub fn links_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let sel_key = egui::Id::new("links_panel_sel");
let mut chosen: Option<u64> = ui.data(|d| d.get_temp(sel_key));
egui::Grid::new("links_grid").num_columns(3).striped(true).spacing([10.0, 4.0]).show(ui, |ui| {
ui.label(egui::RichText::new("Name").color(t.text_dim).size(11.0));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Name")).color(t.text_dim).size(11.0));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Status")).color(t.text_dim).size(11.0));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, "Page · PPI")).color(t.text_dim).size(11.0));
ui.end_row();
@@ -1757,7 +1836,7 @@ pub fn links_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
"embedded" => ("Embedded", t.text_dim),
_ => ("OK", t.text_dim),
};
ui.label(egui::RichText::new(txt).color(col).size(11.0));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::tr(&app.ui.language, txt)).color(col).size(11.0));
let uses = r["uses"].as_array().cloned().unwrap_or_default();
let info = match uses.first() {
Some(u) => {
@@ -1766,11 +1845,14 @@ pub fn links_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let more = if uses.len() > 1 { format!(" (+{})", uses.len() - 1) } else { String::new() };
format!("{page}{ppi}{more}")
}
None => "unused".into(),
None => crate::i18n::tr(&app.ui.language, "unused").into(),
};
// Low effective resolution for print is flagged like the Preflight check.
let low = uses.first().and_then(|u| u["ppi"].as_f64()).is_some_and(|p| p < 150.0);
ui.label(egui::RichText::new(info).size(11.0).color(if low { egui::Color32::from_rgb(0xe8, 0xb3, 0x2c) } else { t.text_dim }));
crate::rtl::label(
ui,
egui::RichText::new(info).size(11.0).color(if low { egui::Color32::from_rgb(0xe8, 0xb3, 0x2c) } else { t.text_dim }),
);
ui.end_row();
}
});
@@ -1802,7 +1884,9 @@ pub fn links_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
// Choose any file in the folder: missing links are looked up there by name.
if ui
.button(crate::i18n::tr(&app.ui.language, "Relink to Folder…"))
.on_hover_text("Pick a file in the folder holding the moved graphics")
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Pick a file in the folder holding the moved graphics"));
})
.clicked()
&& let Some(path) = app.services.pick_open.as_mut().and_then(|pick| pick("relink"))
&& let Some(dir) = std::path::Path::new(&path).parent()
@@ -1821,17 +1905,34 @@ pub fn info_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
match sel_info(app) {
Some(i) => {
let f = |v: f64| designcraft_geom::format_measure(v, u);
ui.label(format!("X: {} Y: {}", f(i.page_rect.x0), f(i.page_rect.y0)));
ui.label(format!("W: {} H: {}", f(i.page_rect.width()), f(i.page_rect.height())));
ui.label(format!("Fill: {} Stroke: {}", i.fill, i.stroke));
crate::rtl::label(ui, format!("X: {} Y: {}", f(i.page_rect.x0), f(i.page_rect.y0)));
crate::rtl::label(ui, format!("W: {} H: {}", f(i.page_rect.width()), f(i.page_rect.height())));
crate::rtl::label(
ui,
format!("{}: {} {}: {}", crate::i18n::tr(&app.ui.language, "Fill"), i.fill, crate::i18n::tr(&app.ui.language, "Stroke"), i.stroke),
);
}
None => {
if let Some(st) = app.session.active() {
ui.label(format!("{} pages · {} stories · {} items", st.doc.page_count(), st.doc.stories.len(), st.doc.all_items().len()));
crate::rtl::label(
ui,
format!(
"{} {} · {} {} · {} {}",
st.doc.page_count(),
crate::i18n::tr(&app.ui.language, "pages"),
st.doc.stories.len(),
crate::i18n::tr(&app.ui.language, "stories"),
st.doc.all_items().len(),
crate::i18n::tr(&app.ui.language, "items")
),
);
}
}
}
ui.label(egui::RichText::new(format!("Render {:.1} ms", app.perf.render_ms)).color(t.text_dim));
crate::rtl::label(
ui,
egui::RichText::new(format!("{} {:.1} ms", crate::i18n::tr(&app.ui.language, "Render"), app.perf.render_ms)).color(t.text_dim),
);
}
pub fn effects_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
@@ -1860,14 +1961,17 @@ pub fn effects_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
B::Luminosity,
];
ui.horizontal(|ui| {
egui::ComboBox::from_id_salt("blend").selected_text(it.blend.label()).width(130.0).show_ui(ui, |ui| {
for m in MODES {
if ui.selectable_label(m == it.blend, m.label()).clicked() {
let _ = app.run("object.opacity", json!({"opacity": it.opacity, "blend": m}));
egui::ComboBox::from_id_salt("blend")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, it.blend.label())))
.width(130.0)
.show_ui(ui, |ui| {
for m in MODES {
if ui.selectable_label(m == it.blend, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, m.label()))).clicked() {
let _ = app.run("object.opacity", json!({"opacity": it.opacity, "blend": m}));
}
}
}
});
caption(ui, "Opacity");
});
caption(ui, crate::i18n::tr(&app.ui.language, "Opacity"));
if let Some(v) = number(ui, "eop", Some(it.opacity as f64 * 100.0), "%", 46.0, 0) {
let _ = app.run("object.opacity", json!({"opacity": (v / 100.0).clamp(0.0, 1.0)}));
}
@@ -1880,11 +1984,11 @@ pub fn effects_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
}
if ds.on {
egui::Grid::new("dsg").num_columns(4).spacing(vec2(6.0, 4.0)).show(ui, |ui| {
caption(ui, "Distance");
caption(ui, crate::i18n::tr(&app.ui.language, "Distance"));
if let Some(v) = number(ui, "dsd", Some(ds.distance), " pt", 56.0, 1) {
let _ = app.run("object.dropShadow", json!({"on": true, "distance": v}));
}
caption(ui, "Angle");
caption(ui, crate::i18n::tr(&app.ui.language, "Angle"));
let global = app.session.active().map_or(120.0, |st| st.doc.settings.global_light);
if let Some(v) = number(ui, "dsa", Some(if ds.global_light { global } else { ds.angle }), "°", 50.0, 0) {
// With Use Global Light on, the angle is the document's (every such shadow moves).
@@ -1895,19 +1999,19 @@ pub fn effects_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
};
}
ui.end_row();
ui.label("");
ui.label("");
crate::rtl::label(ui, "");
crate::rtl::label(ui, "");
caption(ui, "");
let mut gl = ds.global_light;
if ui.checkbox(&mut gl, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Use Global Light"))).changed() {
let _ = app.run("object.dropShadow", json!({"on": true, "globalLight": gl}));
}
ui.end_row();
caption(ui, "Opacity");
caption(ui, crate::i18n::tr(&app.ui.language, "Opacity"));
if let Some(v) = number(ui, "dso", Some(ds.opacity as f64 * 100.0), "%", 56.0, 0) {
let _ = app.run("object.dropShadow", json!({"on": true, "opacity": v / 100.0}));
}
caption(ui, "Size");
caption(ui, crate::i18n::tr(&app.ui.language, "Size"));
if let Some(v) = number(ui, "dss", Some(ds.size), " pt", 50.0, 1) {
let _ = app.run("object.dropShadow", json!({"on": true, "size": v}));
}
@@ -1927,7 +2031,19 @@ pub fn preflight_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
4.5,
if errors == 0 { egui::Color32::from_rgb(60, 200, 90) } else { egui::Color32::from_rgb(235, 50, 50) },
);
ui.label(if issues.is_empty() { "No errors".to_string() } else { format!("{errors} error(s), {} warning(s)", issues.len() - errors) });
crate::rtl::label(
ui,
if issues.is_empty() {
crate::i18n::tr(&app.ui.language, "No errors").to_string()
} else {
format!(
"{errors} {}, {} {}",
crate::i18n::tr(&app.ui.language, "errors"),
issues.len() - errors,
crate::i18n::tr(&app.ui.language, "warnings")
)
},
);
});
divider(ui);
for i in issues {
@@ -1935,7 +2051,7 @@ pub fn preflight_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let text = format!("{} {}", if i.severity == "error" { "⛔" } else { "⚠" }, i.message);
let resp = ui.add(egui::Button::new(egui::RichText::new(text).size(11.0)).frame(false));
if let Some(pg) = &page {
ui.label(egui::RichText::new(format!(" page {pg}")).size(10.0).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(format!(" page {pg}")).size(10.0).color(t.text_dim));
}
if resp.clicked() {
if let Some(id) = i.item {
@@ -1963,7 +2079,7 @@ pub fn attributes_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
egui::Grid::new("attributes_panel").num_columns(2).spacing(vec2(16.0, 6.0)).show(ui, |ui| {
for (i, (key, label, on)) in rows.into_iter().enumerate() {
let mut v = on;
if ui.checkbox(&mut v, label).changed() {
if ui.checkbox(&mut v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).changed() {
let _ = app.run("object.attributes", json!({ key: v }));
}
if i % 2 == 1 {
+37 -7
View File
@@ -17,10 +17,11 @@ fn list(app: &mut DesignApp, ui: &mut egui::Ui, para: bool) {
let attrs = super::text_attrs(app);
let current = attrs.as_ref().and_then(|a| a[if para { "paragraphStyle" } else { "characterStyle" }].as_str().map(str::to_string));
let overrides = attrs.as_ref().map(|a| a[if para { "paraOverrides" } else { "charOverrides" }].as_u64().unwrap_or(0)).unwrap_or(0);
ui.label(
crate::rtl::label(
ui,
egui::RichText::new(match &current {
Some(c) => format!("{c}{}", if overrides > 0 { "+" } else { "" }),
None => "No text selected".into(),
Some(c) => format!("{}{}", crate::i18n::style_name(&app.ui.language, c), if overrides > 0 { "+" } else { "" }),
None => crate::i18n::tr(&app.ui.language, "No text selected").into(),
})
.size(11.0)
.color(t.text_dim),
@@ -52,7 +53,15 @@ fn list(app: &mut DesignApp, ui: &mut egui::Ui, para: bool) {
egui::RichText::new(crate::i18n::tr(&app.ui.language, "⌥-click clears overrides")).size(10.5).color(t.text_disabled),
));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if icons::button(ui, "plus", 20.0, false, "Create New Style from Selection").clicked() {
if icons::button(
ui,
"plus",
20.0,
false,
crate::i18n::tr(&app.ui.language, crate::i18n::tr(&app.ui.language, "Create New Style from Selection")),
)
.clicked()
{
let cmd = if para { "style.paragraph.create" } else { "style.character.create" };
let base = if para { "Paragraph Style 1" } else { "Character Style 1" };
let _ = app.run(cmd, json!({"name": base, "fromSelection": true}));
@@ -79,7 +88,14 @@ fn style_row(app: &mut DesignApp, ui: &mut egui::Ui, para: bool, n: &str, curren
ui.painter().rect_filled(row, 0.0, t.hover);
}
let shown = n.rsplit('/').next().unwrap_or(n);
ui.painter().text(row.min + vec2(8.0, 11.0), egui::Align2::LEFT_CENTER, shown, egui::FontId::proportional(12.5), t.text);
crate::rtl::paint(
ui.painter(),
row.min + vec2(8.0, 11.0),
egui::Align2::LEFT_CENTER,
if n.contains('/') { shown } else { crate::i18n::style_name(&app.ui.language, shown) },
egui::FontId::proportional(12.5),
t.text,
);
if resp.double_clicked() && para {
app.ui.dialog = Some(crate::dialogs::Dialog::new("paragraphStyleOptions", json!({"name": n})));
} else if resp.clicked() {
@@ -89,7 +105,17 @@ fn style_row(app: &mut DesignApp, ui: &mut egui::Ui, para: bool, n: &str, curren
}
let kind = if para { "paragraph" } else { "character" };
resp.context_menu(|ui| {
if ui.button(format!("Apply \"{shown}\"")).clicked() {
if ui
.button(crate::rtl::widget(
ui,
format!(
"{} \"{}\"",
crate::i18n::tr(&app.ui.language, "Apply"),
if n.contains('/') { shown } else { crate::i18n::style_name(&app.ui.language, shown) }
),
))
.clicked()
{
let cmd = if para { "style.paragraph.apply" } else { "style.character.apply" };
let _ = app.run(cmd, json!({"name": n}));
ui.close();
@@ -113,7 +139,11 @@ fn style_row(app: &mut DesignApp, ui: &mut egui::Ui, para: bool, n: &str, curren
ui.separator();
let id = egui::Id::new(("new_style_group", para));
let mut name: String = ui.data(|d| d.get_temp(id)).unwrap_or_default();
let r = ui.add(egui::TextEdit::singleline(&mut name).hint_text("New group…").desired_width(140.0));
let r = ui.add(
egui::TextEdit::singleline(&mut name)
.hint_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "New group…")))
.desired_width(140.0),
);
if r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) && !name.trim().is_empty() {
let _ = app.run("style.group", json!({"kind": kind, "names": [n], "group": name.trim()}));
name.clear();
+28 -14
View File
@@ -14,7 +14,12 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let text = st.selection.text.is_some();
let fill_cur = super::sel_info(app).map(|i| i.fill);
ui.horizontal(|ui| {
ui.label(egui::RichText::new(if text { "Applies to text" } else { "Applies to fill" }).size(11.0).color(t.text_dim));
crate::rtl::label(
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, if text { "Applies to text" } else { "Applies to fill" }))
.size(11.0)
.color(t.text_dim),
);
});
// Unnamed colours (mixed in the Color panel) aren't listed until Add to Swatches; swatches in
// colour groups are listed under their folder.
@@ -65,7 +70,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
egui::RichText::new(crate::i18n::tr(&app.ui.language, "Right-click: stroke, groups")).size(10.5).color(t.text_disabled),
));
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if icons::button(ui, "plus", 20.0, false, "New Swatch").clicked() {
if icons::button(ui, "plus", 20.0, false, crate::i18n::tr(&app.ui.language, "New Swatch")).clicked() {
let _ = app.run("swatch.create", json!({"color": {"c": 0, "m": 50, "y": 100, "k": 0}}));
}
ui.menu_button("☰", |ui| {
@@ -308,7 +313,7 @@ pub fn color_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
spectrum(app, ui, target);
ui.horizontal(|ui| {
let unnamed = sw.as_ref().is_some_and(|w| w.hidden);
if ui.add_enabled(unnamed, egui::Button::new("Add to Swatches")).clicked() {
if ui.add_enabled(unnamed, egui::Button::new(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Add to Swatches")))).clicked() {
let _ = app.run("swatch.addToSwatches", json!({"target": target}));
}
});
@@ -413,15 +418,24 @@ pub fn gradient_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
};
let radial = g.kind == designcraft_color::GradientKind::Radial;
ui.horizontal(|ui| {
crate::widgets::caption(ui, "Type:");
egui::ComboBox::from_id_salt("grad_type").selected_text(g.kind.label()).width(100.0).show_ui(ui, |ui| {
for (k, v) in [("Linear", "linear"), ("Radial", "radial")] {
if ui.selectable_label(g.kind.label() == k, k).clicked() {
let _ = app.run("object.gradient", json!({"kind": v}));
crate::widgets::caption(ui, crate::i18n::tr(&app.ui.language, "Type:"));
egui::ComboBox::from_id_salt("grad_type")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, g.kind.label())))
.width(100.0)
.show_ui(ui, |ui| {
for (k, v) in [("Linear", "linear"), ("Radial", "radial")] {
if ui.selectable_label(g.kind.label() == k, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, k))).clicked() {
let _ = app.run("object.gradient", json!({"kind": v}));
}
}
}
});
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Reverse"))).on_hover_text("Reverse the gradient").clicked() {
});
if ui
.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Reverse")))
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Reverse the gradient"));
})
.clicked()
{
let _ = app.run("object.gradient", json!({"reverse": true}));
}
});
@@ -429,7 +443,7 @@ pub fn gradient_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let drag_id = egui::Id::new("grad_drag");
let mut sel: usize = ui.data(|d| d.get_temp(sel_id)).unwrap_or(0).min(g.stops.len().saturating_sub(1));
ui.horizontal(|ui| {
crate::widgets::caption(ui, "Location:");
crate::widgets::caption(ui, crate::i18n::tr(&app.ui.language, "Location:"));
let loc = g.stops.get(sel).map(|s| s.offset as f64 * 100.0);
if let Some(v) = crate::widgets::number(ui, "grad_loc", loc, " %", 54.0, 1) {
let mut stops = stops_json(&g);
@@ -437,7 +451,7 @@ pub fn gradient_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let _ = app.run("object.gradient", json!({"stops": stops}));
}
if !radial {
crate::widgets::caption(ui, "Angle:");
crate::widgets::caption(ui, crate::i18n::tr(&app.ui.language, "Angle:"));
let angle = fill.gradient_vector.map(|[x0, y0, x1, y1]| (-(y1 - y0)).atan2(x1 - x0).to_degrees()).or(fill.gradient_angle).unwrap_or(0.0);
if let Some(v) = crate::widgets::number(ui, "grad_angle", Some(angle), "°", 54.0, 1) {
let _ = app.run("object.gradient", json!({"angle": v}));
@@ -546,7 +560,7 @@ pub fn gradient_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
// The selected stop's colour.
if let Some(s) = g.stops.get(sel) {
ui.horizontal(|ui| {
crate::widgets::caption(ui, "Stop Color:");
crate::widgets::caption(ui, crate::i18n::tr(&app.ui.language, "Stop Color:"));
let [r, gg, bb] = s.color.to_rgb();
let mut rgb = [(r * 255.0) as u8, (gg * 255.0) as u8, (bb * 255.0) as u8];
if egui::color_picker::color_edit_button_srgb(ui, &mut rgb).changed() {
+24 -20
View File
@@ -32,19 +32,19 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let _ = app.run(id, p);
};
ui.horizontal(|ui| {
caption(ui, "Cell Type");
if ui.small_button("Graphic…").clicked()
caption(ui, crate::i18n::tr(&app.ui.language, "Cell Type"));
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Graphic…"))).clicked()
&& let Err(e) = app.run("app.graphicCell", json!({}))
{
app.status(format!("Table: {e}"));
}
if ui.small_button("Text").clicked() {
if ui.small_button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, "Text"))).clicked() {
let _ = app.run("table.textCell", json!({}));
}
});
// Move the target row / column (InDesign drags them).
ui.horizontal(|ui| {
caption(ui, "Move");
caption(ui, crate::i18n::tr(&app.ui.language, "Move"));
let (nr, nc) = (rows.len(), cols.len());
for (label, id, from, to, ok) in [
("Row ↑", "table.moveRow", r0, r0.saturating_sub(1), r0 > 0),
@@ -68,7 +68,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
let ccur = cell["style"].as_str().filter(|s| !s.is_empty()).unwrap_or(designcraft_doc::NO_CELL_STYLE).to_string();
for (label, kind, names, cur) in [("Table Style", "table", tnames, tcur), ("Cell Style", "cell", cnames, ccur)] {
ui.horizontal(|ui| {
caption(ui, label);
caption(ui, crate::i18n::tr(&app.ui.language, label));
egui::ComboBox::from_id_salt(("tp_style", kind)).selected_text(&cur).width(130.0).show_ui(ui, |ui| {
for n in &names {
if ui.selectable_label(*n == cur, n).clicked() {
@@ -87,7 +87,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
}
}
egui::Grid::new("table_panel").num_columns(4).spacing([6.0, 6.0]).show(ui, |ui| {
caption(ui, "Rows");
caption(ui, crate::i18n::tr(&app.ui.language, "Rows"));
if let Some(v) = number(ui, "tp_rows", Some(rows.len() as f64), "", 52.0, 0) {
let n = v.round().max(1.0) as usize;
if n > rows.len() {
@@ -100,7 +100,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
run(app, "table.deleteRow", json!({"table": tid, "rows": [n, rows.len() - 1]}));
}
}
caption(ui, "Columns");
caption(ui, crate::i18n::tr(&app.ui.language, "Columns"));
if let Some(v) = number(ui, "tp_cols", Some(cols.len() as f64), "", 52.0, 0) {
let n = v.round().max(1.0) as usize;
if n > cols.len() {
@@ -116,19 +116,23 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.end_row();
let row = rows.get(r0).cloned().unwrap_or(Value::Null);
let exactly = row["mode"] == "exactly";
caption(ui, "Row Height");
egui::ComboBox::from_id_salt("tp_rowmode").selected_text(if exactly { "Exactly" } else { "At Least" }).width(70.0).show_ui(ui, |ui| {
for (label, mode) in [("At Least", "atLeast"), ("Exactly", "exactly")] {
if ui.selectable_label((mode == "exactly") == exactly, label).clicked() {
run(app, "table.setRowHeight", json!({"mode": mode}));
caption(ui, crate::i18n::tr(&app.ui.language, "Row Height"));
egui::ComboBox::from_id_salt("tp_rowmode")
.selected_text(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, if exactly { "Exactly" } else { "At Least" })))
.width(70.0)
.show_ui(ui, |ui| {
for (label, mode) in [("At Least", "atLeast"), ("Exactly", "exactly")] {
if ui.selectable_label((mode == "exactly") == exactly, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked()
{
run(app, "table.setRowHeight", json!({"mode": mode}));
}
}
}
});
});
if let Some(h) = measure(ui, "tp_rowh", row["height"].as_f64(), unit, 64.0) {
run(app, "table.setRowHeight", json!({"height": h}));
}
ui.end_row();
caption(ui, "Column Width");
caption(ui, crate::i18n::tr(&app.ui.language, "Column Width"));
ui.label("");
if let Some(w) = measure(ui, "tp_colw", cols.get(c0).and_then(|c| c["width"].as_f64()), unit, 64.0) {
run(app, "table.setColumnWidth", json!({"width": w}));
@@ -136,10 +140,10 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
ui.end_row();
});
divider(ui);
caption(ui, "Cell Text");
caption(ui, crate::i18n::tr(&app.ui.language, "Cell Text"));
ui.horizontal(|ui| {
for (label, vj) in [("Top", "top"), ("Center", "center"), ("Bottom", "bottom"), ("Justify", "justify")] {
if ui.selectable_label(cell["vj"] == vj, label).clicked() {
if ui.selectable_label(cell["vj"] == vj, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
run(app, "table.setCell", json!({"vj": vj}));
}
}
@@ -149,7 +153,7 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
egui::Grid::new("table_insets").num_columns(4).spacing([6.0, 6.0]).show(ui, |ui| {
let labels = ["Top", "Left", "Bottom", "Right"];
for (i, l) in labels.iter().enumerate() {
caption(ui, l);
caption(ui, crate::i18n::tr(&app.ui.language, l));
if let Some(v) = measure(ui, &format!("tp_inset{i}"), insets.get(i).copied(), unit, 56.0) {
let mut n = insets.clone();
n.resize(4, 4.0);
@@ -197,11 +201,11 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
run(app, "table.options", json!({"repeatHeader": rep}));
}
ui.horizontal(|ui| {
caption(ui, "Header Rows");
caption(ui, crate::i18n::tr(&app.ui.language, "Header Rows"));
if let Some(v) = number(ui, "tp_hdr", info["headerRows"].as_f64(), "", 40.0, 0) {
run(app, "table.options", json!({"headerRows": v.max(0.0) as u64}));
}
caption(ui, "Footer Rows");
caption(ui, crate::i18n::tr(&app.ui.language, "Footer Rows"));
if let Some(v) = number(ui, "tp_ftr", info["footerRows"].as_f64(), "", 40.0, 0) {
run(app, "table.options", json!({"footerRows": v.max(0.0) as u64}));
}
+8 -4
View File
@@ -36,7 +36,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
for (i, r) in story.para_ranges().iter().enumerate() {
let lines = story.text[r.clone()].len() / 60 + 1;
let style = &story.paras[i].style;
ui.label(egui::RichText::new(style).size(11.0).color(t.text_dim));
crate::rtl::label(ui, egui::RichText::new(crate::i18n::style_name(&app.ui.language, style)).size(11.0).color(t.text_dim));
for _ in 1..lines {
ui.label(egui::RichText::new(" ").size(11.0));
}
@@ -70,9 +70,13 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context) {
ui,
egui::RichText::new(crate::i18n::tr(&app.ui.language, "OVERSET")).color(egui::Color32::from_rgb(230, 40, 40)).strong(),
));
ui.label(
egui::RichText::new(format!("{} characters don't fit", story.text[o.min(story.text.len())..].chars().count()))
.color(t.text_dim),
crate::rtl::label(
ui,
egui::RichText::new(
crate::i18n::tr(&app.ui.language, "{count} characters don't fit")
.replace("{count}", &story.text[o.min(story.text.len())..].chars().count().to_string()),
)
.color(t.text_dim),
);
});
}
+9 -9
View File
@@ -61,7 +61,7 @@ pub fn show(app: &mut DesignApp, ctx: &egui::Context, sel: Rect, canvas: Rect) {
fn split_button(ui: &mut Ui, icon: &str, label: &str) -> (egui::Response, egui::Response) {
let t = Tokens::get(ui.ctx());
let g = ui.painter().layout_no_wrap(label.to_string(), egui::FontId::proportional(11.0), t.text);
let g = crate::rtl::plain(ui.ctx(), label, egui::FontId::proportional(11.0), t.text);
let w = 26.0 + g.size().x + 8.0 + widgets::SEG_W;
let (r, _) = ui.allocate_exact_size(vec2(w, FIELD_H), Sense::hover());
let main = Rect::from_min_max(r.min, pos2(r.max.x - widgets::SEG_W, r.max.y));
@@ -84,7 +84,7 @@ fn split_button(ui: &mut Ui, icon: &str, label: &str) -> (egui::Response, egui::
fn text_controls(app: &mut DesignApp, ui: &mut Ui, in_text: bool) {
let t = Tokens::get(ui.ctx());
if !in_text {
let (main, seg) = split_button(ui, "fit-text", "Fit Text");
let (main, seg) = split_button(ui, "fit-text", crate::i18n::tr(&app.ui.language, "Fit Text"));
if main
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, "Fit frame height to text"));
@@ -95,7 +95,7 @@ fn text_controls(app: &mut DesignApp, ui: &mut Ui, in_text: bool) {
}
egui::Popup::menu(&seg).show(|ui| {
for (label, v) in [("Off", "off"), ("Height Only", "heightOnly"), ("Width Only", "widthOnly"), ("Height and Width", "heightAndWidth")] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("object.textFrameOptions", json!({"autoSize": v}));
ui.close();
}
@@ -155,7 +155,7 @@ fn text_controls(app: &mut DesignApp, ui: &mut Ui, in_text: bool) {
"leftJustified" | "centerJustified" | "rightJustified" | "fullyJustified" => "palign-justify-left",
_ => "palign-left",
};
let resp = widgets::icon_toggle(ui, icon, false, "Paragraph alignment");
let resp = widgets::icon_toggle(ui, icon, false, crate::i18n::tr(&app.ui.language, "Paragraph alignment"));
egui::Popup::menu(&resp).show(|ui| {
for (label, v) in [
("Align Left", "left"),
@@ -164,13 +164,13 @@ fn text_controls(app: &mut DesignApp, ui: &mut Ui, in_text: bool) {
("Justify", "leftJustified"),
("Justify All", "fullyJustified"),
] {
if ui.selectable_label(al == v, label).clicked() {
if ui.selectable_label(al == v, crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("type.para", json!({"attrs": {"align": v}}));
ui.close();
}
}
});
if widgets::icon_toggle(ui, "frame-options", false, "Text Frame Options").clicked() {
if widgets::icon_toggle(ui, "frame-options", false, crate::i18n::tr(&app.ui.language, "Text Frame Options")).clicked() {
app.ui.dialog = Some(crate::dialogs::Dialog::new("textFrameOptions", json!({})));
}
}
@@ -185,7 +185,7 @@ fn object_controls(app: &mut DesignApp, ui: &mut Ui) {
("fit-frame", "fitFrameToContent", "Fit Frame to Content"),
("fit-center", "centerContent", "Center Content"),
] {
if widgets::icon_toggle(ui, icon, false, tip).clicked() {
if widgets::icon_toggle(ui, icon, false, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run("object.fit", json!({"mode": mode}));
}
}
@@ -221,11 +221,11 @@ fn object_controls(app: &mut DesignApp, ui: &mut Ui) {
}
sep(ui);
for (cmd, icon, tip) in [("object.bringToFront", "objalign-top", "Bring to Front"), ("object.sendToBack", "objalign-bottom", "Send to Back")] {
if widgets::icon_toggle(ui, icon, false, tip).clicked() {
if widgets::icon_toggle(ui, icon, false, crate::i18n::tr(&app.ui.language, tip)).clicked() {
let _ = app.run(cmd, json!({}));
}
}
if i.count > 1 && widgets::outline_button(ui, "Group", 52.0).clicked() {
if i.count > 1 && widgets::outline_button(ui, crate::i18n::tr(&app.ui.language, "Group"), 52.0).clicked() {
let _ = app.run("object.group", json!({}));
}
}
+24 -8
View File
@@ -24,8 +24,14 @@ pub fn show(app: &mut DesignApp, ui: &mut egui::Ui) {
// Collapse chevrons.
ui.horizontal(|ui| {
ui.add_space(width / 2.0 - 8.0);
if icons::button(ui, if cols == 1 { "double-chevron-right" } else { "double-chevron-left" }, 16.0, false, "Toggle double column")
.clicked()
if icons::button(
ui,
if cols == 1 { "double-chevron-right" } else { "double-chevron-left" },
16.0,
false,
crate::i18n::tr(&app.ui.language, "Toggle double column"),
)
.clicked()
{
app.ui.tools_double_column = !app.ui.tools_double_column;
}
@@ -266,7 +272,13 @@ fn fill_stroke_proxy(app: &mut DesignApp, ui: &mut egui::Ui, width: f32) {
ui.painter().rect(r, 1.5, t.well, Stroke::new(1.0, t.well_rim), StrokeKind::Inside);
}
icons::paint(ui.painter(), r.shrink(1.5), icon, if on { t.text_strong } else { t.icon });
if ui.interact(r, ui.id().with(("proxy_fmt", k)), Sense::click()).on_hover_text(tip).clicked() {
if ui
.interact(r, ui.id().with(("proxy_fmt", k)), Sense::click())
.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, tip));
})
.clicked()
{
ui.data_mut(|d| d.insert_temp(text_id, k == 1));
}
}
@@ -287,7 +299,7 @@ fn fill_stroke_proxy(app: &mut DesignApp, ui: &mut egui::Ui, width: f32) {
}
egui::Popup::context_menu(&resp).show(|ui| {
for (label, sw) in [("Apply Color", "[Black]"), ("Apply None", "[None]")] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run(target, json!({"swatch": sw}));
ui.close();
}
@@ -297,7 +309,7 @@ fn fill_stroke_proxy(app: &mut DesignApp, ui: &mut egui::Ui, width: f32) {
separator(ui, &t);
// Utility (frame options).
ui.vertical_centered(|ui| {
if tool_button(ui, "frame-options", false, "Text Frame Options").clicked() {
if tool_button(ui, "frame-options", false, crate::i18n::tr(&app.ui.language, "Text Frame Options")).clicked() {
app.ui.dialog = Some(crate::dialogs::Dialog::new("textFrameOptions", json!({})));
}
});
@@ -315,13 +327,15 @@ fn fill_stroke_proxy(app: &mut DesignApp, ui: &mut egui::Ui, width: f32) {
t.text_strong,
);
flyout_triangle(ui.painter(), well, t.icon);
let resp = resp.on_hover_text(if preview { "Preview (W)" } else { "Normal (W)" });
let resp = resp.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(&app.ui.language, if preview { "Preview (W)" } else { "Normal (W)" }));
});
if resp.clicked() {
let _ = app.run("view.togglePreview", json!({}));
}
egui::Popup::context_menu(&resp).show(|ui| {
for (label, mode) in [("Normal", "normal"), ("Preview", "preview"), ("Bleed", "bleed"), ("Slug", "slug"), ("Presentation", "presentation")] {
if ui.button(label).clicked() {
if ui.button(crate::rtl::widget(ui, crate::i18n::tr(&app.ui.language, label))).clicked() {
let _ = app.run("view.screenMode", json!({"mode": mode}));
ui.close();
}
@@ -356,5 +370,7 @@ fn tool_button(ui: &mut egui::Ui, icon: &str, active: bool, tip: &str) -> egui::
ui.painter().rect_filled(egui::Rect::from_center_size(r.center(), egui::vec2(28.0, 20.0)), 2.0, t.hover);
}
icons::paint(ui.painter(), egui::Rect::from_center_size(r.center(), egui::vec2(17.0, 17.0)), icon, if active { t.text_strong } else { t.icon });
resp.on_hover_text(tip)
resp.on_hover_ui(|ui| {
crate::rtl::label(ui, tip);
})
}
+12 -4
View File
@@ -359,7 +359,7 @@ pub fn link_label(ui: &mut Ui, text: &str, width: f32) -> Response {
}
/// `•••` more-options, right-aligned on its own row at the bottom of a section.
pub fn more_options(ui: &mut Ui) -> Response {
pub fn more_options(ui: &mut Ui, language: &str) -> Response {
let t = Tokens::get(ui.ctx());
let (row, _) = ui.allocate_exact_size(vec2(ui.available_width(), 14.0), Sense::hover());
let r = Rect::from_min_max(pos2(row.max.x - 26.0, row.min.y), pos2(row.max.x - 4.0, row.max.y));
@@ -367,7 +367,9 @@ pub fn more_options(ui: &mut Ui) -> Response {
for i in 0..3 {
ui.painter().circle_filled(pos2(r.min.x + 4.0 + i as f32 * 7.0, r.center().y), 1.9, if resp.hovered() { t.text_strong } else { t.icon });
}
resp.on_hover_text("More Options")
resp.on_hover_ui(|ui| {
crate::rtl::label(ui, crate::i18n::tr(language, "More Options"));
})
}
/// A small dim label (field captions like `X:` or `W:`).
@@ -514,7 +516,13 @@ pub fn icon_toggle_sized(ui: &mut Ui, icon: &str, on: bool, tip: &str, size: egu
}
let s = (size.y - 4.0).min(17.0);
crate::icons::paint(ui.painter(), Rect::from_center_size(r.center(), vec2(s, s)), icon, if on { t.text_strong } else { t.icon });
if tip.is_empty() { resp } else { resp.on_hover_text(tip) }
if tip.is_empty() {
resp
} else {
resp.on_hover_ui(|ui| {
crate::rtl::label(ui, tip);
})
}
}
/// An InDesign checkbox: 12 pt rounded box, white check, label.
@@ -552,7 +560,7 @@ pub fn segmented(ui: &mut Ui, labels: &[&str], active: usize, width: f32) -> Opt
} else if resp.hovered() {
ui.painter().rect_filled(sr, 0.0, t.hover);
}
ui.painter().text(sr.center(), egui::Align2::CENTER_CENTER, *l, label_font(), if i == active { t.accent } else { t.text });
crate::rtl::paint(ui.painter(), sr.center(), egui::Align2::CENTER_CENTER, l, label_font(), if i == active { t.accent } else { t.text });
if i > 0 {
ui.painter().line_segment([sr.left_top(), sr.left_bottom()], Stroke::new(1.0, t.field_border));
}