Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ui-egui/src/animtimeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub fn dragged_duration(duration_ms: u32, dx: f32, pps: f32, factor: f64) -> u32

/// Tooltip text for a bar: `Start: 0.5s, End: 2.5s`.
pub fn label(bar: &Bar) -> String {
format!("Start: {:.2}s, End: {:.2}s", bar.start, bar.start + bar.span)
crate::tl!("Start: {}s, End: {}s", format!("{:.2}", bar.start), format!("{:.2}", bar.start + bar.span))
}

#[cfg(test)]
Expand Down
198 changes: 126 additions & 72 deletions crates/ui-egui/src/backstage.rs

Large diffs are not rendered by default.

91 changes: 48 additions & 43 deletions crates/ui-egui/src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub fn show(app: &mut SlideApp, ui: &mut Ui) {
});
if st.doc.slides.is_empty() && st.selection.target == Target::Slides {
let resp = ui.allocate_rect(avail, Sense::click());
ui.painter().text(avail.center(), Align2::CENTER_CENTER, "Click to add first slide", theme::font(22.0), t.text_dim);
ui.painter().text(avail.center(), Align2::CENTER_CENTER, crate::tl!("Click to add first slide"), theme::font(22.0), t.text_dim);
if resp.clicked() {
let _ = app.run("slide.new", json!({"layout": "title"}));
}
Expand Down Expand Up @@ -745,8 +745,9 @@ fn ruler_markers(app: &mut SlideApp, ui: &mut Ui, p: &egui::Painter, avail: Rect
ui.data_mut(|d| d.insert_temp(kind_id, kind));
}
let (kind_align, kind_label) = KINDS[kind % KINDS.len()];
let kind_label = crate::i18n::t(kind_label);
cr.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, kind_label));
let cr = cr.on_hover_text(format!("{kind_label} (click to change)"));
let cr = cr.on_hover_text(crate::tl!("{kind_label} (click to change)", kind_label = kind_label));
p.rect_stroke(
corner.shrink(2.0),
CornerRadius::same(2),
Expand Down Expand Up @@ -844,14 +845,14 @@ fn draw_guides(app: &SlideApp, painter: &egui::Painter, xf: Xf, slide: Rect, t:
if drag.is_some_and(|(di, _)| di == i) {
let centre = if g.vertical { size.width / 2.0 } else { size.height / 2.0 };
let off = pos - centre;
let side = match (g.vertical, off < 0.0, off > 0.0) {
(true, true, _) => " left of centre",
(true, _, true) => " right of centre",
(false, true, _) => " above centre",
(false, _, true) => " below centre",
_ => " (centre)",
let off_pt = format!("{:.0}", off.abs());
let label = match (g.vertical, off < 0.0, off > 0.0) {
(true, true, _) => crate::tl!("{off_pt} pt left of centre", off_pt = off_pt),
(true, _, true) => crate::tl!("{off_pt} pt right of centre", off_pt = off_pt),
(false, true, _) => crate::tl!("{off_pt} pt above centre", off_pt = off_pt),
(false, _, true) => crate::tl!("{off_pt} pt below centre", off_pt = off_pt),
_ => crate::tl!("{off_pt} pt (centre)", off_pt = off_pt),
};
let label = format!("{:.0} pt{side}", off.abs());
let at = if g.vertical { pos2(a.x + 6.0, slide.min.y + 6.0) } else { pos2(slide.min.x + 6.0, a.y + 4.0) };
let galley = painter.layout_no_wrap(label, theme::font(11.0), t.text);
let r = Rect::from_min_size(at, galley.size()).expand(3.0);
Expand Down Expand Up @@ -1286,13 +1287,17 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
ui.separator();
} else if let Some((path, index)) = app.session.tool.edit_vertex {
let cur = deckcraft_engine::cmd::points::kind_of(&sh, path, index);
for (l, k) in [("Smooth Point", "smooth"), ("Straight Point", "straight"), ("Corner Point", "corner")] {
for (l, k) in [
(crate::tl!("Smooth Point"), "smooth"),
(crate::tl!("Straight Point"), "straight"),
(crate::tl!("Corner Point"), "corner"),
] {
if ui.radio(cur == Some(k), l).clicked() {
let _ = app.run("shape.pointType", json!({"path": path, "index": index, "type": k}));
ui.close();
}
}
if ui.button("Delete Point").clicked() {
if ui.button(crate::tl!("Delete Point")).clicked() {
let _ = app.run("shape.deletePoint", json!({"path": path, "index": index}));
app.session.tool.edit_vertex = None;
ui.close();
Expand All @@ -1309,7 +1314,7 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
}
ui.separator();
}
if ui.button("Exit Edit Points").clicked() {
if ui.button(crate::tl!("Exit Edit Points")).clicked() {
let _ = app.run("shape.editPoints", json!({"on": false}));
ui.close();
}
Expand All @@ -1318,7 +1323,7 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
// A misspelt word under the pointer: its suggestions, then Ignore All and Add to Dictionary.
if let Some(w) = app.squiggles.menu_word.clone() {
if let Some(from) = w.delete_from {
if ui.button(egui::RichText::new("Delete Repeated Word").strong()).clicked() {
if ui.button(egui::RichText::new(crate::tl!("Delete Repeated Word")).strong()).clicked() {
let cut: String = format!("{}{}", " ".repeat(w.start - from), w.word);
// The gap is spaces; take it from the text so tabs or no-break spaces match.
let gap = app
Expand Down Expand Up @@ -1355,7 +1360,7 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
ui.close();
}
} else if w.suggestions.is_empty() {
ui.label(egui::RichText::new("(no spelling suggestions)").weak());
ui.label(egui::RichText::new(crate::tl!("(no spelling suggestions)")).weak());
}
for to in &w.suggestions {
if ui.button(egui::RichText::new(to).strong()).clicked() {
Expand All @@ -1371,12 +1376,12 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
ui.close();
}
}
if ui.button("Ignore All").clicked() {
if ui.button(crate::tl!("Ignore All")).clicked() {
app.squiggles.ignore(&w.word);
app.squiggles.menu_word = None;
ui.close();
}
if ui.button("Add to Dictionary").clicked() {
if ui.button(crate::tl!("Add to Dictionary")).clicked() {
if let Err(e) = app.session.execute("review.addToDictionary", &json!({"word": w.word})) {
app.set_status(e.to_string());
}
Expand All @@ -1386,7 +1391,7 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
ui.separator();
}
let has_sel = app.session.active().is_some_and(|d| !d.selection.shapes.is_empty());
for (l, id) in [("Cut", "edit.cut"), ("Copy", "edit.copy"), ("Paste", "edit.paste")] {
for (l, id) in [(crate::tl!("Cut"), "edit.cut"), (crate::tl!("Copy"), "edit.copy"), (crate::tl!("Paste"), "edit.paste")] {
if ui.add_enabled(crate::ribbon::enabled(app, id), egui::Button::new(l)).clicked() {
let _ = app.run(id, json!({}));
ui.close();
Expand All @@ -1396,126 +1401,126 @@ fn context_menu(app: &mut SlideApp, resp: &egui::Response) {
// Right-clicking the text being edited keeps it (`PointerKind::ContextClick`): text items.
let editing = app.session.active().is_some_and(|d| d.selection.text.as_ref().is_some_and(|t| !t.notes));
if editing {
if ui.button("Paragraph…").clicked() {
if ui.button(crate::tl!("Paragraph…")).clicked() {
app.dialog = Some(crate::dialogs::Dialog::new("paragraph"));
ui.close();
}
for (l, id) in [("Bullets", "format.bullets"), ("Numbering", "format.numbering")] {
for (l, id) in [(crate::tl!("Bullets"), "format.bullets"), (crate::tl!("Numbering"), "format.numbering")] {
if ui.add_enabled(crate::ribbon::enabled(app, id), egui::Button::new(l)).clicked() {
let _ = app.run(id, json!({}));
ui.close();
}
}
if ui.button("Link…").clicked() {
if ui.button(crate::tl!("Link…")).clicked() {
app.dialog = Some(crate::dialogs::Dialog::new("hyperlink"));
ui.close();
}
ui.separator();
if ui.button("Format Shape…").clicked() {
if ui.button(crate::tl!("Format Shape…")).clicked() {
let _ = app.run("view.pane", json!({"pane": "format"}));
ui.close();
}
} else if has_sel {
if ui.button("Edit Text").clicked() {
if ui.button(crate::tl!("Edit Text")).clicked() {
let _ = app.run("text.edit", json!({}));
ui.close();
}
if crate::ribbon::enabled(app, "shape.editPoints")
&& app.session.active().is_some_and(|d| d.selection.shapes.len() == 1 && d.selected_shapes().iter().all(|s| !s.is_line()))
&& ui.button("Edit Points").clicked()
&& ui.button(crate::tl!("Edit Points")).clicked()
{
let _ = app.run("shape.editPoints", json!({"on": true}));
ui.close();
}
if ui.button("Font…").clicked() {
if ui.button(crate::tl!("Font…")).clicked() {
let _ = app.run("format.fontDialog", json!({}));
ui.close();
}
if ui.button("Paragraph…").clicked() {
if ui.button(crate::tl!("Paragraph…")).clicked() {
let _ = app.run("format.paragraphDialog", json!({}));
ui.close();
}
ui.menu_button("Bring to Front", |ui| {
ui.menu_button(crate::tl!("Bring to Front"), |ui| {
crate::ribbon::menu_item(app, ui, crate::icons::Icon::BringToFront, "Bring to Front", "arrange.bringToFront", json!({}));
crate::ribbon::menu_item(app, ui, crate::icons::Icon::BringForward, "Bring Forward", "arrange.bringForward", json!({}));
});
ui.menu_button("Send to Back", |ui| {
ui.menu_button(crate::tl!("Send to Back"), |ui| {
crate::ribbon::menu_item(app, ui, crate::icons::Icon::SendToBack, "Send to Back", "arrange.sendToBack", json!({}));
crate::ribbon::menu_item(app, ui, crate::icons::Icon::SendBackward, "Send Backward", "arrange.sendBackward", json!({}));
});
ui.menu_button("Group", |ui| {
ui.menu_button(crate::tl!("Group"), |ui| {
crate::ribbon::menu_item(app, ui, crate::icons::Icon::Group, "Group", "arrange.group", json!({}));
crate::ribbon::menu_item(app, ui, crate::icons::Icon::Ungroup, "Ungroup", "arrange.ungroup", json!({}));
});
let locked = app.session.active().is_some_and(|d| d.selected_shapes().iter().all(|s| s.locked));
if ui.button(if locked { "Unlock" } else { "Lock" }).clicked() {
if ui.button(if locked { crate::tl!("Unlock") } else { crate::tl!("Lock") }).clicked() {
let _ = app.run("shape.lock", json!({"locked": !locked}));
ui.close();
}
let picture = app
.session
.active()
.is_some_and(|d| d.selected_shapes().iter().any(|s| matches!(s.kind, deckcraft_model::ShapeKind::Picture { .. })));
if picture && ui.button("Change Picture…").clicked() {
if picture && ui.button(crate::tl!("Change Picture…")).clicked() {
let _ = app.run("app.changePictureDialog", json!({}));
ui.close();
}
if ui.button("Link…").clicked() {
if ui.button(crate::tl!("Link…")).clicked() {
app.dialog = Some(crate::dialogs::Dialog::new("hyperlink"));
ui.close();
}
if ui.button("Edit Alt Text…").clicked() {
if ui.button(crate::tl!("Edit Alt Text…")).clicked() {
app.dialog = Some(crate::dialogs::Dialog::new("altText"));
ui.close();
}
if ui.button("Set as Default Shape").clicked() {
if ui.button(crate::tl!("Set as Default Shape")).clicked() {
let _ = app.run("shape.setDefault", json!({}));
ui.close();
}
if ui.button("Size and Position…").clicked() {
if ui.button(crate::tl!("Size and Position…")).clicked() {
let _ = app.run("shape.sizePosition", json!({}));
ui.close();
}
if ui.button("Format Shape…").clicked() {
if ui.button(crate::tl!("Format Shape…")).clicked() {
let _ = app.run("view.pane", json!({"pane": "format"}));
ui.close();
}
} else {
ui.menu_button("Layout", |ui| {
ui.menu_button(crate::tl!("Layout"), |ui| {
let layouts: Vec<(String, u32)> = app
.session
.active()
.and_then(|d| d.doc.masters.first().map(|m| m.layouts.iter().map(|l| (l.name.clone(), l.id.0)).collect()))
.unwrap_or_default();
for (n, id) in layouts {
if ui.button(n).clicked() {
if ui.button(crate::i18n::t(&n)).clicked() {
let _ = app.run("slide.layout", json!({"layout": id}));
ui.close();
}
}
});
ui.menu_button("Grid and Guides", |ui| {
ui.menu_button(crate::tl!("Grid and Guides"), |ui| {
let mut on = app.ui.guides;
if ui.checkbox(&mut on, "Guides").clicked() {
if ui.checkbox(&mut on, crate::tl!("Guides")).clicked() {
let _ = app.run("view.guides", json!({"on": on}));
}
for (label, vertical) in [("Add Vertical Guide", true), ("Add Horizontal Guide", false)] {
for (label, vertical) in [(crate::tl!("Add Vertical Guide"), true), (crate::tl!("Add Horizontal Guide"), false)] {
if ui.button(label).clicked() {
let _ = app.run("guide.add", json!({"vertical": vertical}));
let _ = app.run("view.guides", json!({"on": true}));
ui.close();
}
}
});
if ui.button("Reset Slide").clicked() {
if ui.button(crate::tl!("Reset Slide")).clicked() {
let _ = app.run("slide.reset", json!({}));
ui.close();
}
if ui.button("Format Background…").clicked() {
if ui.button(crate::tl!("Format Background…")).clicked() {
let _ = app.run("view.pane", json!({"pane": "background"}));
ui.close();
}
if ui.button("New Comment").clicked() {
if ui.button(crate::tl!("New Comment")).clicked() {
app.dialog = Some(crate::dialogs::Dialog::new("comment"));
ui.close();
}
Expand Down
17 changes: 15 additions & 2 deletions crates/ui-egui/src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,28 @@ pub fn handle(app: &mut SlideApp, ctx: &egui::Context, req: &ControlRequest) ->
if let Some(ft) = s("formatTab") {
app.ui.format_tab = ft.to_string();
}
if let Some(lang) = s("language").or(s("lang")) {
let norm = crate::i18n::Lang::normalize_pref(lang);
app.ui.language = norm.to_owned();
crate::i18n::set_current(crate::i18n::Lang::from_pref(norm));
}
ok(serde_json::to_value(&app.ui).unwrap_or_default())
}
"ui.dialog.close" => {
app.dialog = None;
if let Some(d) = app.dialog.take()
&& d.id == "preferences"
{
crate::dialogs::restore_preferences(app, &d);
}
ok(Value::Null)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
}
"ui.dialog.open" => {
let Some(id) = s("id") else { return err("missing `id`") };
let mut d = crate::dialogs::Dialog::new(id);
let mut d = if id == "preferences" {
crate::dialogs::Dialog::preferences(app)
} else {
crate::dialogs::Dialog::new(id)
};
if let Some(fields) = p.get("fields").and_then(Value::as_object) {
for (k, v) in fields {
let v = v.as_str().map_or_else(|| v.to_string(), str::to_string);
Expand Down
Loading