Refactor ui ribbon for strict Clippy checks

This commit is contained in:
Dylan M. Taylor
2026-10-28 01:06:48 +00:00
parent 9c0f61356c
commit ac2cc0c4ef
18 changed files with 1078 additions and 536 deletions
+5 -5
View File
@@ -1,6 +1,6 @@
use super::common::mi;
use super::language::mi_check;
use super::*;
use super::{Ui, WordApp, json, menu_button};
/// Word's text wrapping styles in menu order: label, model value, `arrange.wrap` parameter.
pub(crate) const WRAP_STYLES: [(&str, wordcraft_doc::para::Wrap, &str); 7] = {
@@ -40,7 +40,7 @@ pub(super) fn wrap_menu(ui: &mut Ui, app: &mut WordApp) {
let (wrap, side) = wordcraft_engine::cmd::objects::selected(&app.session)
.and_then(|(_, o)| o.frame().map(|(_, _, f)| (f.wrap, f.wrap_text)))
.unwrap_or_default();
menu_button(ui, app, "wrapText", Some("Wrap\nText"), "Wrap Text", true, "arrange.wrapText", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("wrapText", Some("Wrap\nText"), "Wrap Text", true, "arrange.wrapText"), |ui, app| {
use wordcraft_doc::para::{Wrap, WrapText};
for (l, w, v) in [
("In Line with Text", Wrap::Inline, "inline"),
@@ -68,7 +68,7 @@ pub(super) fn wrap_menu(ui: &mut Ui, app: &mut WordApp) {
}
pub(super) fn rotate_menu(ui: &mut Ui, app: &mut WordApp, label: Option<&str>) {
menu_button(ui, app, "rotate", label, "Rotate", false, "arrange.rotate", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("rotate", label, "Rotate", false, "arrange.rotate"), |ui, app| {
mi(ui, app, "Rotate Right 90°", "arrange.rotate", json!({"direction": "right"}));
mi(ui, app, "Rotate Left 90°", "arrange.rotate", json!({"direction": "left"}));
mi(ui, app, "Flip Vertical", "arrange.rotate", json!({"direction": "flipVertical"}));
@@ -92,7 +92,7 @@ pub(super) fn rotation_field(ui: &mut Ui, app: &mut WordApp) {
/// Arrange › Group: Group (Shift+click objects to select several) and Ungroup.
pub(super) fn group_menu(ui: &mut Ui, app: &mut WordApp, label: Option<&str>) {
menu_button(ui, app, "group", label, "Group", false, "arrange.group", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("group", label, "Group", false, "arrange.group"), |ui, app| {
mi(ui, app, "Group", "arrange.group", json!({}));
mi(ui, app, "Ungroup", "arrange.ungroup", json!({}));
});
@@ -101,7 +101,7 @@ pub(super) fn group_menu(ui: &mut Ui, app: &mut WordApp, label: Option<&str>) {
/// Arrange › Align: horizontal position relative to the margin (#3242 — a plain button ran
/// `arrange.align` with no `value`, which always centers; a menu reaches Left and Right too).
pub(super) fn align_menu(ui: &mut Ui, app: &mut WordApp, label: Option<&str>) {
menu_button(ui, app, "align", label, "Align", false, "arrange.align", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("align", label, "Align", false, "arrange.align"), |ui, app| {
mi(ui, app, "Align Left", "arrange.align", json!({"value": "left"}));
mi(ui, app, "Align Center", "arrange.align", json!({"value": "center"}));
mi(ui, app, "Align Right", "arrange.align", json!({"value": "right"}));
+30 -18
View File
@@ -1,6 +1,6 @@
use super::common::{graphic_size, stack};
use super::language::mi_check;
use super::*;
use super::{Rect, Sense, Stroke, Tokens, Ui, WordApp, big, group, json, menu_button, pos2, small, vec2};
/// SmartArt Design: the selected graphic's items (Add Shape, Promote, Demote, Text Pane), its
/// layout (a gallery of previews), its colours and Reset.
@@ -14,9 +14,16 @@ pub(super) fn smart_art_design(app: &mut WordApp, ui: &mut Ui) {
big(ui, app, "addShape", "Add\nShape", "smartArt.addShape", current.map_or(json!({}), |i| json!({"after": i})), false);
stack(ui, |ui| {
let idx = current.map_or(json!({}), |i| json!({"index": i}));
small(ui, app, "promote", Some("Promote"), "Promote", "smartArt.promote", idx.clone(), false);
small(ui, app, "demote", Some("Demote"), "Demote", "smartArt.demote", idx, false);
small(ui, app, "textPane", Some("Text Pane"), "Text Pane", "smartArt.textPane", json!({}), app.session.view.smart_art_pane);
small(ui, app, "promote", Some("Promote"), "Promote", crate::widgets::ButtonAction::new("smartArt.promote", idx.clone(), false));
small(ui, app, "demote", Some("Demote"), "Demote", crate::widgets::ButtonAction::new("smartArt.demote", idx, false));
small(
ui,
app,
"textPane",
Some("Text Pane"),
"Text Pane",
crate::widgets::ButtonAction::new("smartArt.textPane", json!({}), app.session.view.smart_art_pane),
);
});
});
group(ui, "Layouts", None, app, |ui, app| {
@@ -44,18 +51,23 @@ pub(super) fn smart_art_design(app: &mut WordApp, ui: &mut Ui) {
});
});
group(ui, "SmartArt Styles", None, app, |ui, app| {
menu_button(ui, app, "smartArtColors", Some("Change\nColors"), "Change Colors", true, "", |ui, app| {
for colors in SmartArtColors::ALL {
let on = g.colors == colors;
let resp = ui.add(egui::Button::new(crate::dialogs_smart_art::colors_label(colors)).selected(on).min_size(vec2(200.0, 24.0)));
let sw = Rect::from_min_size(pos2(resp.rect.right() - 74.0, resp.rect.center().y - 6.0), vec2(66.0, 12.0));
crate::dialogs_smart_art::swatches(ui.painter(), sw, colors, &theme);
if resp.clicked() {
let _ = app.run("smartArt.colors", json!({"variant": colors.id()}));
ui.close();
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("smartArtColors", Some("Change\nColors"), "Change Colors", true, ""),
|ui, app| {
for colors in SmartArtColors::ALL {
let on = g.colors == colors;
let resp = ui.add(egui::Button::new(crate::dialogs_smart_art::colors_label(colors)).selected(on).min_size(vec2(200.0, 24.0)));
let sw = Rect::from_min_size(pos2(resp.rect.right() - 74.0, resp.rect.center().y - 6.0), vec2(66.0, 12.0));
crate::dialogs_smart_art::swatches(ui.painter(), sw, colors, &theme);
if resp.clicked() {
let _ = app.run("smartArt.colors", json!({"variant": colors.id()}));
ui.close();
}
}
}
});
},
);
});
group(ui, "Reset", None, app, |ui, app| {
big(ui, app, "resetGraphic", "Reset\nGraphic", "smartArt.reset", json!({}), false);
@@ -68,12 +80,12 @@ pub(super) fn smart_art_design(app: &mut WordApp, ui: &mut Ui) {
pub(super) fn chart_design(app: &mut WordApp, ui: &mut Ui) {
let Some(chart) = wordcraft_engine::cmd::chart::selected_chart(&app.session) else { return };
group(ui, "Chart Layouts", None, app, |ui, app| {
menu_button(ui, app, "chartTitle", Some("Chart Title"), "Chart Title", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("chartTitle", Some("Chart Title"), "Chart Title", true, ""), |ui, app| {
mi_check(ui, app, "None", chart.title.is_none(), "chart.title", json!({"text": null}));
let text = chart.title.clone().unwrap_or_else(|| tl!("Chart Title").to_string());
mi_check(ui, app, "Above Chart", chart.title.is_some(), "chart.title", json!({"text": text}));
});
menu_button(ui, app, "legend", Some("Legend"), "Legend", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("legend", Some("Legend"), "Legend", true, ""), |ui, app| {
mi_check(ui, app, "None", chart.legend.is_none(), "chart.legend", json!({"pos": "none"}));
for (label, pos) in [
("Right", wordcraft_doc::chart::LegendPos::Right),
@@ -84,7 +96,7 @@ pub(super) fn chart_design(app: &mut WordApp, ui: &mut Ui) {
mi_check(ui, app, label, chart.legend == Some(pos), "chart.legend", json!({"pos": pos.id()}));
}
});
menu_button(ui, app, "dataLabels", Some("Data Labels"), "Data Labels", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("dataLabels", Some("Data Labels"), "Data Labels", true, ""), |ui, app| {
mi_check(ui, app, "None", !chart.data_labels, "chart.dataLabels", json!({"on": false}));
mi_check(ui, app, "Show", chart.data_labels, "chart.dataLabels", json!({"on": true}));
});
+2 -2
View File
@@ -1,5 +1,5 @@
use super::language::mi_check;
use super::*;
use super::{Align2, Rect, Sense, Tokens, Ui, Value, WordApp, big, group, json, small, vec2};
/// Custom groups (Customize Ribbon, #379): up to three commands as large buttons, more as small
/// labelled buttons in columns of three. Each shows the command's own icon and label.
@@ -17,7 +17,7 @@ pub(super) fn custom_groups(app: &mut WordApp, ui: &mut Ui, groups: &[wordcraft_
for column in ids.chunks(3) {
stack(ui, |ui| {
for (id, label) in column {
small(ui, app, crate::widgets::command_icon(id), Some(label), label, id, json!({}), false);
small(ui, app, crate::widgets::command_icon(id), Some(label), label, crate::widgets::ButtonAction::new(id, json!({}), false));
}
});
}
+83 -45
View File
@@ -1,5 +1,5 @@
use super::common::{mi, mi_text, stack};
use super::*;
use super::{CONTENT_H, Rect, Sense, Target, Ui, WordApp, big, group, json, menu_button, small, vec2};
/// The Design tab's style set gallery in a box of its own, sized from its thumbnails and clipped
/// to it, with a gap on each side: the Colors and Fonts column can't be drawn over, or cut by,
@@ -17,7 +17,7 @@ pub(super) fn style_set_box(app: &mut WordApp, ui: &mut Ui) -> (Rect, Rect) {
pub(super) fn design(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Themes", None, app, |ui, app| {
menu_button(ui, app, "themes", Some("Themes"), "Themes", true, "design.themes", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("themes", Some("Themes"), "Themes", true, "design.themes"), |ui, app| {
for (name, ..) in wordcraft_engine::cmd::design::THEMES {
mi(ui, app, name, "design.theme", json!({"name": name}));
}
@@ -26,55 +26,93 @@ pub(super) fn design(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Document Formatting", None, app, |ui, app| {
style_set_box(app, ui);
stack(ui, |ui| {
menu_button(ui, app, "colors", Some("Colors"), "Theme Colors", false, "design.themeColors", |ui, app| {
for (name, ..) in wordcraft_engine::cmd::design::THEMES {
mi(ui, app, name, "design.themeColors", json!({"name": name}));
}
});
menu_button(ui, app, "fonts", Some("Fonts"), "Theme Fonts", false, "design.themeFonts", |ui, app| {
for (name, h, b, _) in wordcraft_engine::cmd::design::THEMES {
mi_text(ui, app, &format!("{}: {h} / {b}", tl!(name)), "design.themeFonts", json!({"heading": h, "body": b}));
}
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("colors", Some("Colors"), "Theme Colors", false, "design.themeColors"),
|ui, app| {
for (name, ..) in wordcraft_engine::cmd::design::THEMES {
mi(ui, app, name, "design.themeColors", json!({"name": name}));
}
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("fonts", Some("Fonts"), "Theme Fonts", false, "design.themeFonts"),
|ui, app| {
for (name, h, b, _) in wordcraft_engine::cmd::design::THEMES {
mi_text(ui, app, &format!("{}: {h} / {b}", tl!(name)), "design.themeFonts", json!({"heading": h, "body": b}));
}
},
);
});
stack(ui, |ui| {
menu_button(ui, app, "paraSpacing", Some("Paragraph Spacing"), "Paragraph Spacing", false, "design.paragraphSpacing", |ui, app| {
for (l, v) in [
("Default", "default"),
("No Paragraph Space", "none"),
("Compact", "compact"),
("Tight", "tight"),
("Open", "open"),
("Relaxed", "relaxed"),
("Double", "double"),
] {
// "Open" here is roomy spacing, not the file verb: only a `spacing` row
// translates it, else it stays English.
if l == "Open" {
mi_text(ui, app, crate::i18n::get("spacing", l).unwrap_or(l), "design.paragraphSpacing", json!({"value": v}));
continue;
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new(
"paraSpacing",
Some("Paragraph Spacing"),
"Paragraph Spacing",
false,
"design.paragraphSpacing",
),
|ui, app| {
for (l, v) in [
("Default", "default"),
("No Paragraph Space", "none"),
("Compact", "compact"),
("Tight", "tight"),
("Open", "open"),
("Relaxed", "relaxed"),
("Double", "double"),
] {
// "Open" here is roomy spacing, not the file verb: only a `spacing` row
// translates it, else it stays English.
if l == "Open" {
mi_text(ui, app, crate::i18n::get("spacing", l).unwrap_or(l), "design.paragraphSpacing", json!({"value": v}));
continue;
}
mi(ui, app, l, "design.paragraphSpacing", json!({"value": v}));
}
mi(ui, app, l, "design.paragraphSpacing", json!({"value": v}));
}
});
small(ui, app, "effectsDesign", Some("Effects"), "Effects", "design.effects", json!({}), false);
small(ui, app, "setDefault", Some("Set as Default"), "Set as Default", "design.setDefault", json!({}), false);
},
);
small(ui, app, "effectsDesign", Some("Effects"), "Effects", crate::widgets::ButtonAction::new("design.effects", json!({}), false));
small(
ui,
app,
"setDefault",
Some("Set as Default"),
"Set as Default",
crate::widgets::ButtonAction::new("design.setDefault", json!({}), false),
);
});
});
group(ui, "Page Background", None, app, |ui, app| {
menu_button(ui, app, "watermark", Some("Watermark"), "Watermark", true, "design.watermark", |ui, app| {
for w in ["DRAFT", "CONFIDENTIAL", "DO NOT COPY", "SAMPLE", "ASAP", "URGENT"] {
mi(ui, app, w, "design.watermark", json!({"text": w}));
}
ui.separator();
mi(ui, app, "Custom Watermark…", "ui.dialog", json!({"name": "watermark"}));
mi(ui, app, "Remove Watermark", "design.watermark", json!({"remove": true}));
});
menu_button(ui, app, "pageColor", Some("Page\nColor"), "Page Color", true, "design.pageColor", |ui, app| {
mi(ui, app, "No Color", "design.pageColor", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("design.pageColor"));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("watermark", Some("Watermark"), "Watermark", true, "design.watermark"),
|ui, app| {
for w in ["DRAFT", "CONFIDENTIAL", "DO NOT COPY", "SAMPLE", "ASAP", "URGENT"] {
mi(ui, app, w, "design.watermark", json!({"text": w}));
}
ui.separator();
mi(ui, app, "Custom Watermark…", "ui.dialog", json!({"name": "watermark"}));
mi(ui, app, "Remove Watermark", "design.watermark", json!({"remove": true}));
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("pageColor", Some("Page\nColor"), "Page Color", true, "design.pageColor"),
|ui, app| {
mi(ui, app, "No Color", "design.pageColor", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("design.pageColor"));
},
);
big(ui, app, "pageBorders", "Page\nBorders", "design.pageBorders", json!({}), false);
});
}
+4 -4
View File
@@ -1,6 +1,6 @@
use super::common::{mi, stack};
use super::language::mi_check;
use super::*;
use super::{CONTENT_H, Rect, Sense, Stroke, Target, Tokens, Ui, WordApp, big, big_toggle, group, icons, json, menu_button, pos2, split, vec2};
pub(super) fn draw(app: &mut WordApp, ui: &mut Ui) {
use wordcraft_doc::freeform::InkTool;
@@ -25,7 +25,7 @@ pub(super) fn draw(app: &mut WordApp, ui: &mut Ui) {
for (i, pen) in pens.iter().enumerate() {
gallery_pen(ui, app, i, *pen, custom == Some(i));
}
menu_button(ui, app, "addPen", Some("Add\nPen"), "Add Pen", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("addPen", Some("Add\nPen"), "Add Pen", true, ""), |ui, app| {
for (label, kind) in [("Pen", InkTool::Pen), ("Pencil", InkTool::Pencil), ("Highlighter", InkTool::Highlighter)] {
// A new pen starts as that kind's current colour and thickness.
let cur = app.session.view.draw.settings(kind);
@@ -34,7 +34,7 @@ pub(super) fn draw(app: &mut WordApp, ui: &mut Ui) {
});
stack(ui, |ui| {
let sw = Some(crate::theme::c32(set.color));
split(ui, app, "fontcolor", "Color", pen_command(tool), json!({}), false, sw, |ui, app| {
split(ui, app, "fontcolor", "Color", crate::widgets::ButtonAction::new(pen_command(tool), json!({}), false), sw, |ui, app| {
let theme = app.session.doc.settings.theme_colors.clone();
// With ink lasso-selected the colour goes to the selection, else to the pen.
let cmd = if lasso_on { "draw.lassoColor" } else { pen_command(tool) };
@@ -43,7 +43,7 @@ pub(super) fn draw(app: &mut WordApp, ui: &mut Ui) {
update_gallery_pen(app, custom, tool);
}
});
menu_button(ui, app, "thickness", None, "Thickness", false, "draw.pen", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("thickness", None, "Thickness", false, "draw.pen"), |ui, app| {
let widths: &[f32] = if tool == InkTool::Highlighter { &[4.0, 8.0, 12.0, 18.0, 24.0] } else { &[0.5, 1.0, 1.5, 2.5, 3.5, 5.0] };
for w in widths {
let label = crate::i18n::fmt(tl!("{n} pt"), &[("n", &w.to_string())]);
+185 -115
View File
@@ -1,13 +1,14 @@
use super::common::{mi, mi_text, stack};
use super::underline::underline_menu;
use super::*;
use super::{Remember, Stroke, Target, Tokens, Ui, Value, WordApp, big, group, json, menu_button, small, split, vec2};
/// The Format Painter button. A click arms it for one use (blue border only); a double-click keeps
/// it on across several uses (full checked look), like Word. Esc or another click turns it off.
fn format_painter_button(ui: &mut Ui, app: &mut WordApp) {
let sticky = matches!(app.session.painter, Some((_, _, true)));
let armed = app.session.painter.is_some();
let resp = small(ui, app, "painter", Some("Format"), "Format Painter", "edit.formatPainter", json!({}), sticky);
let resp =
small(ui, app, "painter", Some("Format"), "Format Painter", crate::widgets::ButtonAction::new("edit.formatPainter", json!({}), sticky));
if resp.double_clicked() {
painter_double_click(app);
} else if armed && !sticky {
@@ -29,15 +30,15 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
let st = app.session.run("format.state", &json!({})).unwrap_or_default();
let flag = |k: &str| st.get(k).and_then(Value::as_bool).unwrap_or(false);
group(ui, "Clipboard", Some("edit.clipboardPane"), app, |ui, app| {
menu_button(ui, app, "paste", Some("Paste"), "Paste (⌘V)", true, "edit.paste", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("paste", Some("Paste"), "Paste (⌘V)", true, "edit.paste"), |ui, app| {
mi(ui, app, "Paste", "edit.paste", json!({}));
mi(ui, app, "Keep Text Only", "edit.pasteText", json!({}));
mi(ui, app, "Merge Formatting", "edit.pasteMerge", json!({}));
mi(ui, app, "Paste Special…", "edit.pasteSpecial", json!({}));
});
stack(ui, |ui| {
small(ui, app, "cut", Some("Cut"), "Cut", "edit.cut", json!({}), false);
small(ui, app, "copy", Some("Copy"), "Copy", "edit.copy", json!({}), false);
small(ui, app, "cut", Some("Cut"), "Cut", crate::widgets::ButtonAction::new("edit.cut", json!({}), false));
small(ui, app, "copy", Some("Copy"), "Copy", crate::widgets::ButtonAction::new("edit.copy", json!({}), false));
format_painter_button(ui, app);
});
});
@@ -49,7 +50,18 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
let fams = app.previews.families();
let recent: Vec<String> = app.ui.recent_fonts.iter().map(|f| app.previews.font_shown(f)).collect();
let prev = app.previews.font_preview_fn();
if let Some(f) = crate::widgets::font_combo_with_recent_hint(ui, "font", 150.0, &font, &fams, &recent, Some(&*prev), tl!("Font")) {
if let Some(f) = crate::widgets::font_combo_with_recent_hint(
ui,
crate::widgets::FontComboOptions {
id: "font",
width: 150.0,
current: &font,
items: &fams,
recent: &recent,
preview: Some(&*prev),
tip: tl!("Font"),
},
) {
let _ = if f == crate::widgets::ADD_FONT {
app.run("font.add", json!({}))
} else if f == crate::widgets::LOAD_SYSTEM_FONTS {
@@ -72,30 +84,45 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
let _ = app.run("format.size", json!({"size": x}));
}
ui.add_space(3.0);
small(ui, app, "grow", None, "Increase Font Size", "format.growFont", json!({}), false);
small(ui, app, "shrink", None, "Decrease Font Size", "format.shrinkFont", json!({}), false);
menu_button(ui, app, "case", None, "Change Case", false, "format.changeCase", |ui, app| {
small(ui, app, "grow", None, "Increase Font Size", crate::widgets::ButtonAction::new("format.growFont", json!({}), false));
small(ui, app, "shrink", None, "Decrease Font Size", crate::widgets::ButtonAction::new("format.shrinkFont", json!({}), false));
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("case", None, "Change Case", false, "format.changeCase"), |ui, app| {
mi(ui, app, "Sentence case.", "format.changeCase", json!({"mode": "sentence"}));
mi(ui, app, "lowercase", "format.changeCase", json!({"mode": "lower"}));
mi(ui, app, "UPPERCASE", "format.changeCase", json!({"mode": "upper"}));
mi(ui, app, "Capitalize Each Word", "format.changeCase", json!({"mode": "title"}));
mi(ui, app, "tOGGLE cASE", "format.changeCase", json!({"mode": "toggle"}));
});
small(ui, app, "clear", None, "Clear All Formatting", "format.clear", json!({}), false);
small(ui, app, "phonetic", None, "Phonetic Guide", "format.phonetic", json!({}), false);
small(ui, app, "clear", None, "Clear All Formatting", crate::widgets::ButtonAction::new("format.clear", json!({}), false));
small(ui, app, "phonetic", None, "Phonetic Guide", crate::widgets::ButtonAction::new("format.phonetic", json!({}), false));
});
ui.add_space(3.0);
crate::widgets::row(ui, |ui| {
small(ui, app, "bold", None, "Bold", "format.bold", json!({}), flag("bold"));
small(ui, app, "italic", None, "Italic", "format.italic", json!({}), flag("italic"));
split(ui, app, "underline", "Underline", "format.underline", json!({}), flag("underline"), None, underline_menu);
small(ui, app, "strike", None, "Strikethrough", "format.strikethrough", json!({}), flag("strike"));
small(ui, app, "subscript", None, "Subscript", "format.subscript", json!({}), flag("subscript"));
small(ui, app, "superscript", None, "Superscript", "format.superscript", json!({}), flag("superscript"));
small(ui, app, "charborder", None, "Character Border", "format.border", json!({}), flag("border"));
small(ui, app, "enclose", None, "Enclose Characters", "format.enclose", json!({}), false);
small(ui, app, "bold", None, "Bold", crate::widgets::ButtonAction::new("format.bold", json!({}), flag("bold")));
small(ui, app, "italic", None, "Italic", crate::widgets::ButtonAction::new("format.italic", json!({}), flag("italic")));
split(
ui,
app,
"underline",
"Underline",
crate::widgets::ButtonAction::new("format.underline", json!({}), flag("underline")),
None,
underline_menu,
);
small(ui, app, "strike", None, "Strikethrough", crate::widgets::ButtonAction::new("format.strikethrough", json!({}), flag("strike")));
small(ui, app, "subscript", None, "Subscript", crate::widgets::ButtonAction::new("format.subscript", json!({}), flag("subscript")));
small(
ui,
app,
"superscript",
None,
"Superscript",
crate::widgets::ButtonAction::new("format.superscript", json!({}), flag("superscript")),
);
small(ui, app, "charborder", None, "Character Border", crate::widgets::ButtonAction::new("format.border", json!({}), flag("border")));
small(ui, app, "enclose", None, "Enclose Characters", crate::widgets::ButtonAction::new("format.enclose", json!({}), false));
ui.add_space(4.0);
menu_button(ui, app, "effects", None, "Text Effects and Typography", false, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("effects", None, "Text Effects and Typography", false, ""), |ui, app| {
let theme = app.session.doc.settings.theme_colors.clone();
crate::wordart_menus::style_gallery(ui, app, "format.textEffects");
ui.separator();
@@ -117,19 +144,25 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
app,
icon,
"Text Highlight Color",
"format.highlight",
json!({"color": color}),
false,
crate::widgets::ButtonAction::new("format.highlight", json!({"color": color}), false),
sw,
crate::mini_toolbar::highlight_menu,
);
let fc = app.canvas.last_font_color.clone();
let sw = wordcraft_doc::Rgb::parse(&fc).map(crate::theme::c32);
split(ui, app, "fontcolor", "Font Color", "format.color", json!({"color": fc}), false, sw, |ui, app| {
mi(ui, app, "Automatic", "format.color", json!({"color": "auto"}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("format.color").remember(Remember::FontColor));
});
split(
ui,
app,
"fontcolor",
"Font Color",
crate::widgets::ButtonAction::new("format.color", json!({"color": fc}), false),
sw,
|ui, app| {
mi(ui, app, "Automatic", "format.color", json!({"color": "auto"}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("format.color").remember(Remember::FontColor));
},
);
});
});
});
@@ -141,7 +174,7 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
let lists = wordcraft_engine::cmd::para::list_buttons(&app.session.doc, rp.as_ref().and_then(|r| r.numbering));
stack(ui, |ui| {
crate::widgets::row(ui, |ui| {
split(ui, app, "bullets", "Bullets", "para.bullets", json!({}), lists.bullets, None, |ui, app| {
split(ui, app, "bullets", "Bullets", crate::widgets::ButtonAction::new("para.bullets", json!({}), lists.bullets), None, |ui, app| {
ui.label(egui::RichText::new(tl!("Bullet Library")).small().weak());
ui.horizontal(|ui| {
for c in ["•", "○", "▪", "◆", "➢", "✓", "–"] {
@@ -162,98 +195,135 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
});
mi(ui, app, "None", "para.bullets", json!({"off": true}));
});
split(ui, app, "numbering", "Numbering", "para.numbering", json!({}), lists.numbering, None, |ui, app| {
for (l, k) in [
("1. 2. 3.", "numbered"),
("1) 2) 3)", "numberedParen"),
("I. II. III.", "outline"),
("A. B. C.", "upperLetter"),
("a) b) c)", "lowerLetter"),
("i. ii. iii.", "lowerRoman"),
] {
mi(ui, app, l, "para.numbering", json!({"kind": k}));
}
ui.separator();
mi(ui, app, "Restart at 1", "para.restartNumbering", json!({}));
mi(ui, app, "None", "para.numbering", json!({"off": true}));
});
split(ui, app, "multilevel", "Multilevel List", "para.multilevel", json!({}), lists.multilevel, None, |ui, app| {
mi(ui, app, "1. 1.1. 1.1.1.", "para.multilevel", json!({"kind": "legal"}));
mi(ui, app, "I. A. 1. a.", "para.multilevel", json!({"kind": "outline"}));
ui.separator();
// Lists linked to the heading styles: the headings carry the numbers. Each item
// reads as its first three levels, number then (translated) style name.
for (numbers, k) in [
(["1", "1.1", "1.1.1"], "headings"),
(["Article I.", "Section 1.01", "(a)"], "article"),
(["I.", "A.", "1."], "outlineHeadings"),
(["Chapter 1", "", ""], "chapter"),
] {
let text = numbers
.iter()
.zip(["Heading 1", "Heading 2", "Heading 3"])
.map(|(n, h)| if n.is_empty() { tl!(h).to_string() } else { format!("{} {}", tl!(n), tl!(h)) })
.collect::<Vec<_>>()
.join(" / ");
mi_text(ui, app, &text, "list.multilevelPreset", json!({"preset": k}));
}
ui.separator();
mi(ui, app, "Define New Multilevel List…", "list.define", json!({}));
ui.separator();
for lv in 0..5u64 {
mi(ui, app, &format!("Change to Level {}", lv + 1), "para.listLevel", json!({"level": lv}));
}
});
split(
ui,
app,
"numbering",
"Numbering",
crate::widgets::ButtonAction::new("para.numbering", json!({}), lists.numbering),
None,
|ui, app| {
for (l, k) in [
("1. 2. 3.", "numbered"),
("1) 2) 3)", "numberedParen"),
("I. II. III.", "outline"),
("A. B. C.", "upperLetter"),
("a) b) c)", "lowerLetter"),
("i. ii. iii.", "lowerRoman"),
] {
mi(ui, app, l, "para.numbering", json!({"kind": k}));
}
ui.separator();
mi(ui, app, "Restart at 1", "para.restartNumbering", json!({}));
mi(ui, app, "None", "para.numbering", json!({"off": true}));
},
);
split(
ui,
app,
"multilevel",
"Multilevel List",
crate::widgets::ButtonAction::new("para.multilevel", json!({}), lists.multilevel),
None,
|ui, app| {
mi(ui, app, "1. 1.1. 1.1.1.", "para.multilevel", json!({"kind": "legal"}));
mi(ui, app, "I. A. 1. a.", "para.multilevel", json!({"kind": "outline"}));
ui.separator();
// Lists linked to the heading styles: the headings carry the numbers. Each item
// reads as its first three levels, number then (translated) style name.
for (numbers, k) in [
(["1", "1.1", "1.1.1"], "headings"),
(["Article I.", "Section 1.01", "(a)"], "article"),
(["I.", "A.", "1."], "outlineHeadings"),
(["Chapter 1", "", ""], "chapter"),
] {
let text = numbers
.iter()
.zip(["Heading 1", "Heading 2", "Heading 3"])
.map(|(n, h)| if n.is_empty() { tl!(h).to_string() } else { format!("{} {}", tl!(n), tl!(h)) })
.collect::<Vec<_>>()
.join(" / ");
mi_text(ui, app, &text, "list.multilevelPreset", json!({"preset": k}));
}
ui.separator();
mi(ui, app, "Define New Multilevel List…", "list.define", json!({}));
ui.separator();
for lv in 0..5u64 {
mi(ui, app, &format!("Change to Level {}", lv + 1), "para.listLevel", json!({"level": lv}));
}
},
);
ui.add_space(4.0);
small(ui, app, "outdent", None, "Decrease Indent", "para.outdent", json!({}), false);
small(ui, app, "indent", None, "Increase Indent", "para.indent", json!({}), false);
small(ui, app, "outdent", None, "Decrease Indent", crate::widgets::ButtonAction::new("para.outdent", json!({}), false));
small(ui, app, "indent", None, "Increase Indent", crate::widgets::ButtonAction::new("para.indent", json!({}), false));
ui.add_space(4.0);
small(ui, app, "sort", None, "Sort", "para.sort", json!({}), false);
small(ui, app, "sort", None, "Sort", crate::widgets::ButtonAction::new("para.sort", json!({}), false));
let marks = app.session.view.marks;
small(ui, app, "pilcrow", None, "Show/Hide ¶", "view.marks", json!({}), marks);
small(ui, app, "pilcrow", None, "Show/Hide ¶", crate::widgets::ButtonAction::new("view.marks", json!({}), marks));
});
ui.add_space(3.0);
crate::widgets::row(ui, |ui| {
use wordcraft_doc::Align as A;
small(ui, app, "alignLeft", None, "Align Left", "para.alignLeft", json!({}), align == A::Left);
small(ui, app, "alignCenter", None, "Center", "para.alignCenter", json!({}), align == A::Center);
small(ui, app, "alignRight", None, "Align Right", "para.alignRight", json!({}), align == A::Right);
small(ui, app, "justify", None, "Justify", "para.justify", json!({}), align == A::Justify);
small(ui, app, "alignLeft", None, "Align Left", crate::widgets::ButtonAction::new("para.alignLeft", json!({}), align == A::Left));
small(ui, app, "alignCenter", None, "Center", crate::widgets::ButtonAction::new("para.alignCenter", json!({}), align == A::Center));
small(ui, app, "alignRight", None, "Align Right", crate::widgets::ButtonAction::new("para.alignRight", json!({}), align == A::Right));
small(ui, app, "justify", None, "Justify", crate::widgets::ButtonAction::new("para.justify", json!({}), align == A::Justify));
ui.add_space(4.0);
small(ui, app, "textLtr", None, "Left-to-Right Text Direction", "para.ltr", json!({}), !rtl);
small(ui, app, "textRtl", None, "Right-to-Left Text Direction", "para.rtl", json!({}), rtl);
small(ui, app, "textLtr", None, "Left-to-Right Text Direction", crate::widgets::ButtonAction::new("para.ltr", json!({}), !rtl));
small(ui, app, "textRtl", None, "Right-to-Left Text Direction", crate::widgets::ButtonAction::new("para.rtl", json!({}), rtl));
ui.add_space(4.0);
menu_button(ui, app, "lineSpacing", None, "Line and Paragraph Spacing", false, "para.lineSpacing", |ui, app| {
for v in [1.0, 1.15, 1.5, 2.0, 2.5, 3.0] {
mi(ui, app, &format!("{v}"), "para.lineSpacing", json!({"value": v}));
}
ui.separator();
mi(ui, app, "Add Space Before Paragraph", "para.addSpaceBefore", json!({}));
mi(ui, app, "Remove Space After Paragraph", "para.removeSpaceAfter", json!({}));
mi(ui, app, "Line Spacing Options…", "para.dialog", json!({}));
});
split(ui, app, "shading", "Shading", "para.shading", json!({"color": app.canvas.last_shading.clone()}), false, None, |ui, app| {
mi(ui, app, "No Color", "para.shading", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("para.shading").remember(Remember::Shading));
});
split(ui, app, "borders", "Borders", "para.borders", json!({"kind": "bottom"}), false, None, |ui, app| {
for (l, k) in [
("Bottom Border", "bottom"),
("Top Border", "top"),
("Left Border", "left"),
("Right Border", "right"),
("No Border", "none"),
("All Borders", "all"),
("Outside Borders", "outside"),
("Inside Borders", "inside"),
] {
mi(ui, app, l, "para.borders", json!({"kind": k}));
}
ui.separator();
mi(ui, app, "Horizontal Line", "insert.horizontalLine", json!({}));
mi(ui, app, "Borders and Shading…", "para.borders", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("lineSpacing", None, "Line and Paragraph Spacing", false, "para.lineSpacing"),
|ui, app| {
for v in [1.0, 1.15, 1.5, 2.0, 2.5, 3.0] {
mi(ui, app, &format!("{v}"), "para.lineSpacing", json!({"value": v}));
}
ui.separator();
mi(ui, app, "Add Space Before Paragraph", "para.addSpaceBefore", json!({}));
mi(ui, app, "Remove Space After Paragraph", "para.removeSpaceAfter", json!({}));
mi(ui, app, "Line Spacing Options…", "para.dialog", json!({}));
},
);
split(
ui,
app,
"shading",
"Shading",
crate::widgets::ButtonAction::new("para.shading", json!({"color": app.canvas.last_shading.clone()}), false),
None,
|ui, app| {
mi(ui, app, "No Color", "para.shading", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("para.shading").remember(Remember::Shading));
},
);
split(
ui,
app,
"borders",
"Borders",
crate::widgets::ButtonAction::new("para.borders", json!({"kind": "bottom"}), false),
None,
|ui, app| {
for (l, k) in [
("Bottom Border", "bottom"),
("Top Border", "top"),
("Left Border", "left"),
("Right Border", "right"),
("No Border", "none"),
("All Borders", "all"),
("Outside Borders", "outside"),
("Inside Borders", "inside"),
] {
mi(ui, app, l, "para.borders", json!({"kind": k}));
}
ui.separator();
mi(ui, app, "Horizontal Line", "insert.horizontalLine", json!({}));
mi(ui, app, "Borders and Shading…", "para.borders", json!({}));
},
);
});
});
});
@@ -262,14 +332,14 @@ pub(super) fn home(app: &mut WordApp, ui: &mut Ui) {
});
group(ui, "Editing", None, app, |ui, app| {
stack(ui, |ui| {
menu_button(ui, app, "find", Some("Find"), "Find", false, "edit.find", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("find", Some("Find"), "Find", false, "edit.find"), |ui, app| {
// As in Word: Find searches in the Navigation pane; Advanced Find is the dialog (#712).
mi(ui, app, "Find", "edit.find", json!({}));
mi(ui, app, "Advanced Find…", "edit.advancedFind", json!({}));
mi(ui, app, "Go To…", "ui.dialog", json!({"name": "goto"}));
});
small(ui, app, "replace", Some("Replace"), "Replace", "ui.dialog", json!({"name": "replace"}), false);
menu_button(ui, app, "select", Some("Select"), "Select", false, "select.all", |ui, app| {
small(ui, app, "replace", Some("Replace"), "Replace", crate::widgets::ButtonAction::new("ui.dialog", json!({"name": "replace"}), false));
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("select", Some("Select"), "Select", false, "select.all"), |ui, app| {
mi(ui, app, "Select All", "select.all", json!({}));
mi(ui, app, "Select Paragraph", "select.paragraph", json!({}));
mi(ui, app, "Select Sentence", "select.sentence", json!({}));
+76 -42
View File
@@ -1,19 +1,24 @@
use super::common::{mi, mi_icon, stack};
use super::language::mi_check;
use super::*;
use super::{Ui, WordApp, big, group, json, menu_button, small};
pub(super) fn insert(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Pages", None, app, |ui, app| {
stack(ui, |ui| {
menu_button(ui, app, "coverPage", Some("Cover Page"), "Cover Page", false, "insert.coverPage", |ui, app| {
mi(ui, app, "Studio cover", "insert.coverPage", json!({}));
});
small(ui, app, "blankPage", Some("Blank Page"), "Blank Page", "insert.blankPage", json!({}), false);
small(ui, app, "pageBreak", Some("Page Break"), "Page Break", "insert.pageBreak", json!({}), false);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("coverPage", Some("Cover Page"), "Cover Page", false, "insert.coverPage"),
|ui, app| {
mi(ui, app, "Studio cover", "insert.coverPage", json!({}));
},
);
small(ui, app, "blankPage", Some("Blank Page"), "Blank Page", crate::widgets::ButtonAction::new("insert.blankPage", json!({}), false));
small(ui, app, "pageBreak", Some("Page Break"), "Page Break", crate::widgets::ButtonAction::new("insert.pageBreak", json!({}), false));
});
});
group(ui, "Tables", None, app, |ui, app| {
menu_button(ui, app, "table", Some("Table"), "Add a Table", true, "insert.table", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("table", Some("Table"), "Add a Table", true, "insert.table"), |ui, app| {
crate::dialogs::table_grid_picker(ui, app);
ui.separator();
mi(ui, app, "Insert Table…", "ui.dialog", json!({"name": "insertTable"}));
@@ -29,7 +34,7 @@ pub(super) fn insert(app: &mut WordApp, ui: &mut Ui) {
});
group(ui, "Illustrations", None, app, |ui, app| {
big(ui, app, "picture", "Pictures", "insert.picture", json!({}), false);
menu_button(ui, app, "shapes", Some("Shapes"), "Shapes", true, "insert.shape", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("shapes", Some("Shapes"), "Shapes", true, "insert.shape"), |ui, app| {
for (l, k) in [
("Rectangle", "rectangle"),
("Rounded Rectangle", "roundedRectangle"),
@@ -59,23 +64,37 @@ pub(super) fn insert(app: &mut WordApp, ui: &mut Ui) {
}
});
stack(ui, |ui| {
small(ui, app, "icons", Some("Icons"), "Icons", "insert.icon", json!({}), false);
small(ui, app, "smartArt", Some("SmartArt"), "SmartArt", "insert.smartArt", json!({}), false);
small(ui, app, "chart", Some("Chart"), "Chart", "insert.chart", json!({}), false);
small(ui, app, "icons", Some("Icons"), "Icons", crate::widgets::ButtonAction::new("insert.icon", json!({}), false));
small(ui, app, "smartArt", Some("SmartArt"), "SmartArt", crate::widgets::ButtonAction::new("insert.smartArt", json!({}), false));
small(ui, app, "chart", Some("Chart"), "Chart", crate::widgets::ButtonAction::new("insert.chart", json!({}), false));
});
});
group(ui, "Links", None, app, |ui, app| {
stack(ui, |ui| {
small(ui, app, "link", Some("Link"), "Link", "ui.dialog", json!({"name": "link"}), false);
small(ui, app, "bookmark", Some("Bookmark"), "Bookmark", "ui.dialog", json!({"name": "bookmark"}), false);
small(ui, app, "crossRef", Some("Cross-reference"), "Cross-reference", "insert.crossReference", json!({}), false);
small(ui, app, "link", Some("Link"), "Link", crate::widgets::ButtonAction::new("ui.dialog", json!({"name": "link"}), false));
small(
ui,
app,
"bookmark",
Some("Bookmark"),
"Bookmark",
crate::widgets::ButtonAction::new("ui.dialog", json!({"name": "bookmark"}), false),
);
small(
ui,
app,
"crossRef",
Some("Cross-reference"),
"Cross-reference",
crate::widgets::ButtonAction::new("insert.crossReference", json!({}), false),
);
});
});
group(ui, "Comments", None, app, |ui, app| {
big(ui, app, "newComment", "Comment", "review.newComment", json!({}), false);
});
group(ui, "Header & Footer", None, app, |ui, app| {
menu_button(ui, app, "header", Some("Header"), "Header", true, "insert.header", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("header", Some("Header"), "Header", true, "insert.header"), |ui, app| {
mi(ui, app, "Blank", "insert.header", json!({"preset": "blank"}));
mi(ui, app, "Blank (Three Columns)", "insert.header", json!({"preset": "blankThree"}));
mi(ui, app, "Document Title", "insert.header", json!({"preset": "title"}));
@@ -83,7 +102,7 @@ pub(super) fn insert(app: &mut WordApp, ui: &mut Ui) {
mi(ui, app, "Edit Header", "insert.editHeader", json!({}));
mi(ui, app, "Remove Header", "insert.removeHeader", json!({}));
});
menu_button(ui, app, "footer", Some("Footer"), "Footer", true, "insert.footer", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("footer", Some("Footer"), "Footer", true, "insert.footer"), |ui, app| {
mi(ui, app, "Blank", "insert.footer", json!({"preset": "blank"}));
mi(ui, app, "Blank (Three Columns)", "insert.footer", json!({"preset": "blankThree"}));
mi(ui, app, "Page Number", "insert.footer", json!({"preset": "pageNumber"}));
@@ -91,45 +110,60 @@ pub(super) fn insert(app: &mut WordApp, ui: &mut Ui) {
mi(ui, app, "Edit Footer", "insert.editFooter", json!({}));
mi(ui, app, "Remove Footer", "insert.removeFooter", json!({}));
});
menu_button(ui, app, "pageNumber", Some("Page\nNumber"), "Page Number", true, "insert.pageNumber", |ui, app| {
mi(ui, app, "Top of Page", "insert.pageNumber", json!({"position": "top", "align": "right"}));
mi(ui, app, "Bottom of Page", "insert.pageNumber", json!({"position": "bottom", "align": "center"}));
mi(ui, app, "Page X of Y", "insert.pageNumber", json!({"position": "bottom", "format": "x of y"}));
mi(ui, app, "Current Position", "insert.pageNumber", json!({"position": "current"}));
mi(ui, app, "Format Page Numbers…", "ui.dialog", json!({"name": "pageNumbers"}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("pageNumber", Some("Page\nNumber"), "Page Number", true, "insert.pageNumber"),
|ui, app| {
mi(ui, app, "Top of Page", "insert.pageNumber", json!({"position": "top", "align": "right"}));
mi(ui, app, "Bottom of Page", "insert.pageNumber", json!({"position": "bottom", "align": "center"}));
mi(ui, app, "Page X of Y", "insert.pageNumber", json!({"position": "bottom", "format": "x of y"}));
mi(ui, app, "Current Position", "insert.pageNumber", json!({"position": "current"}));
mi(ui, app, "Format Page Numbers…", "ui.dialog", json!({"name": "pageNumbers"}));
},
);
});
group(ui, "Text", None, app, |ui, app| {
big(ui, app, "textBox", "Text\nBox", "insert.textBox", json!({}), false);
stack(ui, |ui| {
menu_button(ui, app, "quickParts", Some("Quick Parts"), "Quick Parts", false, "insert.quickParts", |ui, app| {
// This session's building blocks, then the document's own Quick Parts.
let mut parts: Vec<String> = app.session.building_blocks.keys().cloned().collect();
let mut seen: std::collections::HashSet<String> = parts.iter().cloned().collect();
let glossary = app.session.doc.glossary.iter().filter(|e| e.gallery == wordcraft_doc::QUICK_PARTS_GALLERY);
parts.extend(glossary.filter(|e| seen.insert(e.name.clone())).map(|e| e.name.clone()));
for name in parts {
if ui.button(&name).clicked() {
let _ = app.run("insert.quickParts", json!({"insert": name}));
ui.close();
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("quickParts", Some("Quick Parts"), "Quick Parts", false, "insert.quickParts"),
|ui, app| {
// This session's building blocks, then the document's own Quick Parts.
let mut parts: Vec<String> = app.session.building_blocks.keys().cloned().collect();
let mut seen: std::collections::HashSet<String> = parts.iter().cloned().collect();
let glossary = app.session.doc.glossary.iter().filter(|e| e.gallery == wordcraft_doc::QUICK_PARTS_GALLERY);
parts.extend(glossary.filter(|e| seen.insert(e.name.clone())).map(|e| e.name.clone()));
for name in parts {
if ui.button(&name).clicked() {
let _ = app.run("insert.quickParts", json!({"insert": name}));
ui.close();
}
}
}
mi(ui, app, "Field…", "ui.dialog", json!({"name": "field"}));
});
menu_button(ui, app, "wordArt", Some("WordArt"), "WordArt", false, "", |ui, app| {
mi(ui, app, "Field…", "ui.dialog", json!({"name": "field"}));
},
);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("wordArt", Some("WordArt"), "WordArt", false, ""), |ui, app| {
crate::wordart_menus::style_gallery(ui, app, "insert.wordArt")
});
menu_button(ui, app, "dropCap", Some("Drop Cap"), "Drop Cap", false, "insert.dropCap", drop_cap_items);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("dropCap", Some("Drop Cap"), "Drop Cap", false, "insert.dropCap"),
drop_cap_items,
);
});
stack(ui, |ui| {
small(ui, app, "signature", None, "Signature Line", "insert.signatureLine", json!({}), false);
small(ui, app, "dateTime", None, "Date & Time", "insert.dateTime", json!({}), false);
small(ui, app, "object", None, "Object", "insert.object", json!({}), false);
small(ui, app, "signature", None, "Signature Line", crate::widgets::ButtonAction::new("insert.signatureLine", json!({}), false));
small(ui, app, "dateTime", None, "Date & Time", crate::widgets::ButtonAction::new("insert.dateTime", json!({}), false));
small(ui, app, "object", None, "Object", crate::widgets::ButtonAction::new("insert.object", json!({}), false));
});
});
group(ui, "Symbols", None, app, |ui, app| {
crate::equation_tab::insert_button(ui, app);
menu_button(ui, app, "symbol", Some("Symbol"), "Symbol", true, "insert.symbol", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("symbol", Some("Symbol"), "Symbol", true, "insert.symbol"), |ui, app| {
// Recently used symbols first (their font on hover), then common ones.
let mut items: Vec<(String, String)> = app.ui.recent_symbols.iter().map(|r| (r.ch.clone(), r.font.clone())).collect();
for c in [
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Ui, Value, WordApp, json, vec2};
/// The proofing languages the Language menu offers: tag and English name (translated when drawn).
pub(crate) const PROOFING_LANGUAGES: [(&str, &str); 16] = [
+79 -33
View File
@@ -1,7 +1,7 @@
use super::arrange::{align_menu, group_menu, rotate_menu, wrap_menu};
use super::common::{mi, size_menu, stack};
use super::language::mi_check;
use super::*;
use super::{MeasureExt, Ui, WordApp, big, group, json, menu_button, small, vec2};
// Layout › Margins, Orientation, Size and Columns: the caret section's setting has a check
// mark, as Text Direction's does (#1206).
@@ -52,20 +52,32 @@ pub(super) fn layout(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Page Setup", Some("layout.pageSetup"), app, |ui, app| {
// Text Direction works on what the caret is in: table cells, a text box, else the section.
let dir = wordcraft_engine::cmd::page::text_direction_at(&app.session);
menu_button(ui, app, "textDirection", Some("Text\nDirection"), "Text Direction", true, "layout.textDirection", |ui, app| {
use wordcraft_doc::props::TextDirection;
for (label, d) in
[("Horizontal", TextDirection::Horizontal), ("Rotate all text 90°", TextDirection::Down), ("Rotate all text 270°", TextDirection::Up)]
{
mi_check(ui, app, label, dir == d, "layout.textDirection", json!({"value": d.ooxml()}));
}
});
menu_button(ui, app, "margins", Some("Margins"), "Margins", true, "layout.margins", margins_items);
menu_button(ui, app, "orientation", Some("Orientation"), "Orientation", true, "layout.orientation", orientation_items);
menu_button(ui, app, "size", Some("Size"), "Size", true, "layout.size", size_menu);
menu_button(ui, app, "columns", Some("Columns"), "Columns", true, "layout.columns", columns_items);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("textDirection", Some("Text\nDirection"), "Text Direction", true, "layout.textDirection"),
|ui, app| {
use wordcraft_doc::props::TextDirection;
for (label, d) in [
("Horizontal", TextDirection::Horizontal),
("Rotate all text 90°", TextDirection::Down),
("Rotate all text 270°", TextDirection::Up),
] {
mi_check(ui, app, label, dir == d, "layout.textDirection", json!({"value": d.ooxml()}));
}
},
);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("margins", Some("Margins"), "Margins", true, "layout.margins"), margins_items);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("orientation", Some("Orientation"), "Orientation", true, "layout.orientation"),
orientation_items,
);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("size", Some("Size"), "Size", true, "layout.size"), size_menu);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("columns", Some("Columns"), "Columns", true, "layout.columns"), columns_items);
stack(ui, |ui| {
menu_button(ui, app, "breaks", Some("Breaks"), "Breaks", false, "layout.break", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("breaks", Some("Breaks"), "Breaks", false, "layout.break"), |ui, app| {
ui.label(egui::RichText::new(tl!("Page Breaks")).strong());
mi(ui, app, "Page", "layout.break", json!({"kind": "page"}));
mi(ui, app, "Column", "layout.break", json!({"kind": "column"}));
@@ -76,23 +88,36 @@ pub(super) fn layout(app: &mut WordApp, ui: &mut Ui) {
mi(ui, app, "Even Page", "layout.break", json!({"kind": "evenPage"}));
mi(ui, app, "Odd Page", "layout.break", json!({"kind": "oddPage"}));
});
menu_button(ui, app, "lineNumbers", Some("Line Numbers"), "Line Numbers", false, "layout.lineNumbers", |ui, app| {
for (l, v) in
[("None", "none"), ("Continuous", "continuous"), ("Restart Each Page", "restartPage"), ("Restart Each Section", "restartSection")]
{
mi(ui, app, l, "layout.lineNumbers", json!({"value": v}));
}
ui.separator();
mi(ui, app, "Line Numbering Options…", "ui.dialog", json!({"name": "lineNumbers"}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("lineNumbers", Some("Line Numbers"), "Line Numbers", false, "layout.lineNumbers"),
|ui, app| {
for (l, v) in [
("None", "none"),
("Continuous", "continuous"),
("Restart Each Page", "restartPage"),
("Restart Each Section", "restartSection"),
] {
mi(ui, app, l, "layout.lineNumbers", json!({"value": v}));
}
ui.separator();
mi(ui, app, "Line Numbering Options…", "ui.dialog", json!({"name": "lineNumbers"}));
},
);
let hy = app.session.doc.settings.auto_hyphenation;
menu_button(ui, app, "hyphenation", Some("Hyphenation"), "Hyphenation", false, "layout.hyphenation", |ui, app| {
mi(ui, app, if hy { "✓ Automatic" } else { "Automatic" }, "layout.hyphenation", json!({"value": true}));
mi(ui, app, if hy { "None" } else { "✓ None" }, "layout.hyphenation", json!({"value": false}));
mi(ui, app, "Manual…", "ui.dialog", json!({"name": "manualHyphenation"}));
ui.separator();
mi(ui, app, "Hyphenation Options…", "ui.dialog", json!({"name": "hyphenation"}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("hyphenation", Some("Hyphenation"), "Hyphenation", false, "layout.hyphenation"),
|ui, app| {
mi(ui, app, if hy { "✓ Automatic" } else { "Automatic" }, "layout.hyphenation", json!({"value": true}));
mi(ui, app, if hy { "None" } else { "✓ None" }, "layout.hyphenation", json!({"value": false}));
mi(ui, app, "Manual…", "ui.dialog", json!({"name": "manualHyphenation"}));
ui.separator();
mi(ui, app, "Hyphenation Options…", "ui.dialog", json!({"name": "hyphenation"}));
},
);
});
});
group(ui, "Paragraph", Some("para.dialog"), app, |ui, app| {
@@ -147,9 +172,30 @@ pub(super) fn layout(app: &mut WordApp, ui: &mut Ui) {
big(ui, app, "position", "Position", "arrange.position", json!({}), false);
wrap_menu(ui, app);
stack(ui, |ui| {
small(ui, app, "bringForward", Some("Bring Forward"), "Bring Forward", "arrange.bringForward", json!({}), false);
small(ui, app, "sendBackward", Some("Send Backward"), "Send Backward", "arrange.sendBackward", json!({}), false);
small(ui, app, "selectionPane", Some("Selection Pane"), "Selection Pane", "arrange.selectionPane", json!({}), false);
small(
ui,
app,
"bringForward",
Some("Bring Forward"),
"Bring Forward",
crate::widgets::ButtonAction::new("arrange.bringForward", json!({}), false),
);
small(
ui,
app,
"sendBackward",
Some("Send Backward"),
"Send Backward",
crate::widgets::ButtonAction::new("arrange.sendBackward", json!({}), false),
);
small(
ui,
app,
"selectionPane",
Some("Selection Pane"),
"Selection Pane",
crate::widgets::ButtonAction::new("arrange.selectionPane", json!({}), false),
);
});
stack(ui, |ui| {
align_menu(ui, app, None);
+67 -31
View File
@@ -1,6 +1,6 @@
use super::common::{mi, stack};
use super::language::mi_check;
use super::*;
use super::{Ui, WordApp, big, group, json, menu_button, small};
pub(super) fn mailings(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Create", None, app, |ui, app| {
@@ -9,25 +9,35 @@ pub(super) fn mailings(app: &mut WordApp, ui: &mut Ui) {
});
group(ui, "Start Mail Merge", None, app, |ui, app| {
// The kinds of merge document the engine makes, the current one checked.
menu_button(ui, app, "mailMerge", Some("Start Mail\nMerge"), "Start Mail Merge", true, "mailings.start", |ui, app| {
let kind = app.session.merge.kind.clone();
for (label, k) in [
("Letters", "letters"),
("E-mail Messages", "emails"),
("Envelopes", "envelopes"),
("Labels", "labels"),
("Directory", "directory"),
("Normal Word Document", "normal"),
] {
let current = kind == k || (k == "normal" && kind.is_empty());
mi_check(ui, app, label, current, "mailings.start", json!({"kind": k}));
}
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("mailMerge", Some("Start Mail\nMerge"), "Start Mail Merge", true, "mailings.start"),
|ui, app| {
let kind = app.session.merge.kind.clone();
for (label, k) in [
("Letters", "letters"),
("E-mail Messages", "emails"),
("Envelopes", "envelopes"),
("Labels", "labels"),
("Directory", "directory"),
("Normal Word Document", "normal"),
] {
let current = kind == k || (k == "normal" && kind.is_empty());
mi_check(ui, app, label, current, "mailings.start", json!({"kind": k}));
}
},
);
// The command needs data, so the button offers the two ways to give it (#240).
menu_button(ui, app, "recipients", Some("Select\nRecipients"), "Select Recipients", true, "mailings.recipients", |ui, app| {
mi(ui, app, "Type a New List…", "ui.dialog", json!({"name": "newRecipientList"}));
mi(ui, app, "Use an Existing List…", "ui.openRecipientList", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("recipients", Some("Select\nRecipients"), "Select Recipients", true, "mailings.recipients"),
|ui, app| {
mi(ui, app, "Type a New List…", "ui.dialog", json!({"name": "newRecipientList"}));
mi(ui, app, "Use an Existing List…", "ui.openRecipientList", json!({}));
},
);
big(ui, app, "editRecipients", "Edit\nRecipient List", "mailings.editRecipients", json!({}), false);
});
group(ui, "Write & Insert Fields", None, app, |ui, app| {
@@ -37,34 +47,60 @@ pub(super) fn mailings(app: &mut WordApp, ui: &mut Ui) {
big(ui, app, "mergeField", "Insert Merge\nField", "mailings.insertField", json!({}), false);
stack(ui, |ui| {
// The merge rules the engine knows; If and Skip Record If ask for their condition.
menu_button(ui, app, "rules", Some("Rules"), "Rules", false, "mailings.rules", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("rules", Some("Rules"), "Rules", false, "mailings.rules"), |ui, app| {
mi(ui, app, "If…Then…Else…", "mailings.rules", json!({"rule": "IF"}));
mi(ui, app, "Merge Record #", "mailings.rules", json!({"rule": "MERGEREC"}));
mi(ui, app, "Next Record", "mailings.rules", json!({"rule": "NEXT"}));
mi(ui, app, "Skip Record If…", "mailings.rules", json!({"rule": "SKIPIF"}));
});
small(ui, app, "matchFields", Some("Match Fields"), "Match Fields", "mailings.matchFields", json!({}), false);
small(
ui,
app,
"matchFields",
Some("Match Fields"),
"Match Fields",
crate::widgets::ButtonAction::new("mailings.matchFields", json!({}), false),
);
});
});
group(ui, "Preview Results", None, app, |ui, app| {
big(ui, app, "preview", "Preview\nResults", "mailings.preview", json!({}), false);
stack(ui, |ui| {
crate::widgets::row(ui, |ui| {
small(ui, app, "first", None, "First Record", "mailings.first", json!({}), false);
small(ui, app, "previous", None, "Previous Record", "mailings.previous", json!({}), false);
small(ui, app, "first", None, "First Record", crate::widgets::ButtonAction::new("mailings.first", json!({}), false));
small(ui, app, "previous", None, "Previous Record", crate::widgets::ButtonAction::new("mailings.previous", json!({}), false));
record_box(ui, app);
small(ui, app, "next", None, "Next Record", "mailings.next", json!({}), false);
small(ui, app, "last", None, "Last Record", "mailings.last", json!({}), false);
small(ui, app, "next", None, "Next Record", crate::widgets::ButtonAction::new("mailings.next", json!({}), false));
small(ui, app, "last", None, "Last Record", crate::widgets::ButtonAction::new("mailings.last", json!({}), false));
});
small(ui, app, "find", Some("Find Recipient"), "Find Recipient", "mailings.findRecipient", json!({}), false);
small(ui, app, "checkErrors", Some("Check for Errors"), "Check for Errors", "mailings.checkErrors", json!({}), false);
small(
ui,
app,
"find",
Some("Find Recipient"),
"Find Recipient",
crate::widgets::ButtonAction::new("mailings.findRecipient", json!({}), false),
);
small(
ui,
app,
"checkErrors",
Some("Check for Errors"),
"Check for Errors",
crate::widgets::ButtonAction::new("mailings.checkErrors", json!({}), false),
);
});
});
group(ui, "Finish", None, app, |ui, app| {
menu_button(ui, app, "finish", Some("Finish &\nMerge"), "Finish & Merge", true, "mailings.finish", |ui, app| {
mi(ui, app, "Edit Individual Documents…", "mailings.finish", json!({}));
mi(ui, app, "Print Documents…", "mailings.printDocuments", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("finish", Some("Finish &\nMerge"), "Finish & Merge", true, "mailings.finish"),
|ui, app| {
mi(ui, app, "Edit Individual Documents…", "mailings.finish", json!({}));
mi(ui, app, "Print Documents…", "mailings.printDocuments", json!({}));
},
);
});
}
+102 -66
View File
@@ -1,81 +1,117 @@
use super::arrange::{align_menu, group_menu, rotate_menu, rotation_field, wrap_menu};
use super::common::{esc_cancelled, mi, stack};
use super::shape::alt_text_group;
use super::*;
use super::{MeasureExt, Target, Ui, WordApp, big, group, json, menu_button, small};
/// Draws the Picture Format tab's Adjust, Picture Styles, Accessibility (Alt Text), Arrange and
/// Size groups.
pub(super) fn picture_format(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Adjust", None, app, |ui, app| {
menu_button(ui, app, "picture", Some("Corrections"), "Brightness, contrast and sharpness", true, "picture.corrections", |ui, app| {
for (l, b, c) in
[("Brighter +20%", 20.0, 0.0), ("Darker −20%", -20.0, 0.0), ("More Contrast +20%", 0.0, 20.0), ("Less Contrast −20%", 0.0, -20.0)]
{
mi(ui, app, l, "picture.corrections", json!({"brightness": b, "contrast": c}));
}
ui.separator();
mi(ui, app, "Sharpen", "picture.corrections", json!({"sharpen": 40.0}));
mi(ui, app, "Soften", "picture.corrections", json!({"sharpen": -40.0}));
});
menu_button(ui, app, "picture", Some("Color"), "Saturation and recolor", true, "picture.color", |ui, app| {
for (l, v) in [("Full Saturation 100%", 100.0), ("Muted 50%", 50.0), ("Gray 0%", 0.0), ("Vivid 200%", 200.0)] {
mi(ui, app, l, "picture.color", json!({"mode": "saturation", "saturation": v}));
}
ui.separator();
mi(ui, app, "Grayscale", "picture.color", json!({"mode": "grayscale"}));
mi(ui, app, "Sepia", "picture.color", json!({"mode": "sepia"}));
mi(ui, app, "Washout", "picture.color", json!({"mode": "washout"}));
});
menu_button(ui, app, "icons", Some("Icon Color"), "Recolor a one-color picture, such as an icon", true, "picture.iconColor", |ui, app| {
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("picture.iconColor"));
});
menu_button(ui, app, "picture", Some("Transparency"), "Picture transparency", true, "picture.transparency", |ui, app| {
for v in [0.0, 15.0, 30.0, 50.0, 65.0, 80.0, 95.0] {
mi(ui, app, &format!("{v:.0}%"), "picture.transparency", json!({"percent": v}));
}
ui.separator();
// An exact value, dragged with a live preview: absolute from the original
// pixels, one undo step for the whole drag (same pattern as Size › W/H).
// The current value isn't stored anywhere, so the slider reopens where it
// was last left for this picture.
let (media, off) = match wordcraft_engine::cmd::objects::selected(&app.session) {
Some((pos, wordcraft_doc::para::InlineObject::Image { media, .. })) => (media, pos.off),
_ => (String::new(), 0),
};
let key = egui::Id::unique(("picture_transparency", media, off));
let mut v = ui.data(|d| d.get_temp::<f64>(key)).unwrap_or(0.0);
let r = ui.add(egui::DragValue::new(&mut v).speed(1.0).range(0.0..=100.0).suffix("%"));
if r.changed() {
ui.data_mut(|d| d.insert_temp(key, v));
if r.dragged() && !r.drag_started() {
app.session.join_next_undo();
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("picture", Some("Corrections"), "Brightness, contrast and sharpness", true, "picture.corrections"),
|ui, app| {
for (l, b, c) in
[("Brighter +20%", 20.0, 0.0), ("Darker −20%", -20.0, 0.0), ("More Contrast +20%", 0.0, 20.0), ("Less Contrast −20%", 0.0, -20.0)]
{
mi(ui, app, l, "picture.corrections", json!({"brightness": b, "contrast": c}));
}
let _ = app.run("picture.transparency", json!({"percent": v}));
}
});
ui.separator();
mi(ui, app, "Sharpen", "picture.corrections", json!({"sharpen": 40.0}));
mi(ui, app, "Soften", "picture.corrections", json!({"sharpen": -40.0}));
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("picture", Some("Color"), "Saturation and recolor", true, "picture.color"),
|ui, app| {
for (l, v) in [("Full Saturation 100%", 100.0), ("Muted 50%", 50.0), ("Gray 0%", 0.0), ("Vivid 200%", 200.0)] {
mi(ui, app, l, "picture.color", json!({"mode": "saturation", "saturation": v}));
}
ui.separator();
mi(ui, app, "Grayscale", "picture.color", json!({"mode": "grayscale"}));
mi(ui, app, "Sepia", "picture.color", json!({"mode": "sepia"}));
mi(ui, app, "Washout", "picture.color", json!({"mode": "washout"}));
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new(
"icons",
Some("Icon Color"),
"Recolor a one-color picture, such as an icon",
true,
"picture.iconColor",
),
|ui, app| {
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("picture.iconColor"));
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("picture", Some("Transparency"), "Picture transparency", true, "picture.transparency"),
|ui, app| {
for v in [0.0, 15.0, 30.0, 50.0, 65.0, 80.0, 95.0] {
mi(ui, app, &format!("{v:.0}%"), "picture.transparency", json!({"percent": v}));
}
ui.separator();
// An exact value, dragged with a live preview: absolute from the original
// pixels, one undo step for the whole drag (same pattern as Size › W/H).
// The current value isn't stored anywhere, so the slider reopens where it
// was last left for this picture.
let (media, off) = match wordcraft_engine::cmd::objects::selected(&app.session) {
Some((pos, wordcraft_doc::para::InlineObject::Image { media, .. })) => (media, pos.off),
_ => (String::new(), 0),
};
let key = egui::Id::unique(("picture_transparency", media, off));
let mut v = ui.data(|d| d.get_temp::<f64>(key)).unwrap_or(0.0);
let r = ui.add(egui::DragValue::new(&mut v).speed(1.0).range(0.0..=100.0).suffix("%"));
if r.changed() {
ui.data_mut(|d| d.insert_temp(key, v));
if r.dragged() && !r.drag_started() {
app.session.join_next_undo();
}
let _ = app.run("picture.transparency", json!({"percent": v}));
}
},
);
stack(ui, |ui| {
small(ui, app, "picture", Some("Change"), "Change Picture", "ui.changePicture", json!({}), false);
small(ui, app, "picture", Some("Reset"), "Reset Picture", "picture.reset", json!({}), false);
small(ui, app, "picture", Some("Change"), "Change Picture", crate::widgets::ButtonAction::new("ui.changePicture", json!({}), false));
small(ui, app, "picture", Some("Reset"), "Reset Picture", crate::widgets::ButtonAction::new("picture.reset", json!({}), false));
});
});
group(ui, "Picture Styles", None, app, |ui, app| {
menu_button(ui, app, "picture", Some("Styles"), "Frames and effects", true, "picture.style", |ui, app| {
for (l, s) in [
("Simple Frame", "simpleFrame"),
("Thick Frame", "thickFrame"),
("Rounded", "rounded"),
("Soft Edge", "softEdge"),
("Shadow", "shadow"),
] {
mi(ui, app, l, "picture.style", json!({"style": s}));
}
});
menu_button(ui, app, "picture", Some("Border"), "Picture border", true, "picture.border", |ui, app| {
for (l, w) in [("Thin", 2), ("Medium", 4), ("Thick", 8)] {
mi(ui, app, l, "picture.border", json!({"width": w}));
}
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("picture", Some("Styles"), "Frames and effects", true, "picture.style"),
|ui, app| {
for (l, s) in [
("Simple Frame", "simpleFrame"),
("Thick Frame", "thickFrame"),
("Rounded", "rounded"),
("Soft Edge", "softEdge"),
("Shadow", "shadow"),
] {
mi(ui, app, l, "picture.style", json!({"style": s}));
}
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("picture", Some("Border"), "Picture border", true, "picture.border"),
|ui, app| {
for (l, w) in [("Thin", 2), ("Medium", 4), ("Thick", 8)] {
mi(ui, app, l, "picture.border", json!({"width": w}));
}
},
);
});
alt_text_group(ui, app);
group(ui, "Arrange", None, app, |ui, app| {
@@ -131,7 +167,7 @@ pub(super) fn picture_format(app: &mut WordApp, ui: &mut Ui) {
ui.add_space(2.0);
crate::widgets::row(ui, |ui| {
let cropping = crate::crop::active(app).is_some();
small(ui, app, "crop", Some("Crop"), "Crop", "ui.crop", json!({}), cropping);
small(ui, app, "crop", Some("Crop"), "Crop", crate::widgets::ButtonAction::new("ui.crop", json!({}), cropping));
for (i, side) in ["L", "T", "R", "B"].iter().enumerate() {
ui.label(egui::RichText::new(tl!(side)).small());
let mut v = crop0[i] * 100.0;
+111 -37
View File
@@ -1,32 +1,63 @@
use super::common::{mi, mi_text, stack};
use super::language::mi_check;
use super::*;
use super::{Ui, WordApp, big, group, json, menu_button, small};
pub(super) fn references(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Table of Contents", None, app, |ui, app| {
menu_button(ui, app, "toc", Some("Table of\nContents"), "Table of Contents", true, "references.toc", |ui, app| {
mi(ui, app, "Automatic Table 1 (3 levels)", "references.toc", json!({"levels": 3}));
mi(ui, app, "Automatic Table 2 (2 levels)", "references.toc", json!({"levels": 2, "title": "Table of Contents"}));
ui.separator();
mi(ui, app, "Custom Table of Contents…", "ui.dialog", json!({"name": "toc"}));
mi(ui, app, "Remove Table of Contents", "references.removeToc", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("toc", Some("Table of\nContents"), "Table of Contents", true, "references.toc"),
|ui, app| {
mi(ui, app, "Automatic Table 1 (3 levels)", "references.toc", json!({"levels": 3}));
mi(ui, app, "Automatic Table 2 (2 levels)", "references.toc", json!({"levels": 2, "title": "Table of Contents"}));
ui.separator();
mi(ui, app, "Custom Table of Contents…", "ui.dialog", json!({"name": "toc"}));
mi(ui, app, "Remove Table of Contents", "references.removeToc", json!({}));
},
);
stack(ui, |ui| {
menu_button(ui, app, "addText", Some("Add Text"), "Add Text", false, "references.addText", |ui, app| {
mi(ui, app, "Do Not Show in TOC", "references.addText", json!({"level": 0}));
for l in 1..=3u64 {
mi(ui, app, &format!("Level {l}"), "references.addText", json!({"level": l}));
}
});
small(ui, app, "updateTable", Some("Update Table"), "Update Table", "references.updateToc", json!({}), false);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("addText", Some("Add Text"), "Add Text", false, "references.addText"),
|ui, app| {
mi(ui, app, "Do Not Show in TOC", "references.addText", json!({"level": 0}));
for l in 1..=3u64 {
mi(ui, app, &format!("Level {l}"), "references.addText", json!({"level": l}));
}
},
);
small(
ui,
app,
"updateTable",
Some("Update Table"),
"Update Table",
crate::widgets::ButtonAction::new("references.updateToc", json!({}), false),
);
});
});
group(ui, "Footnotes", Some("references.noteOptions"), app, |ui, app| {
big(ui, app, "footnote", "Insert\nFootnote", "references.footnote", json!({}), false);
stack(ui, |ui| {
small(ui, app, "endnote", Some("Insert Endnote"), "Insert Endnote", "references.endnote", json!({}), false);
small(ui, app, "nextFootnote", Some("Next Footnote"), "Next Footnote", "references.nextFootnote", json!({}), false);
small(ui, app, "showNotes", Some("Show Notes"), "Show Notes", "references.notes", json!({}), false);
small(
ui,
app,
"endnote",
Some("Insert Endnote"),
"Insert Endnote",
crate::widgets::ButtonAction::new("references.endnote", json!({}), false),
);
small(
ui,
app,
"nextFootnote",
Some("Next Footnote"),
"Next Footnote",
crate::widgets::ButtonAction::new("references.nextFootnote", json!({}), false),
);
small(ui, app, "showNotes", Some("Show Notes"), "Show Notes", crate::widgets::ButtonAction::new("references.notes", json!({}), false));
});
});
group(ui, "Research", None, app, |ui, app| {
@@ -34,7 +65,7 @@ pub(super) fn references(app: &mut WordApp, ui: &mut Ui) {
});
group(ui, "Citations & Bibliography", None, app, |ui, app| {
// The document's sources to cite, then a new one (Create Source, #402).
menu_button(ui, app, "citation", Some("Insert\nCitation"), "Insert Citation", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("citation", Some("Insert\nCitation"), "Insert Citation", true, ""), |ui, app| {
let sources: Vec<(String, String)> =
app.session.doc.sources.iter().map(|s| (s.tag.clone(), format!("{} — {}", s.author, s.title))).collect();
for (tag, label) in sources {
@@ -46,15 +77,34 @@ pub(super) fn references(app: &mut WordApp, ui: &mut Ui) {
mi(ui, app, "Add New Source…", "ui.dialog", json!({"name": "createSource"}));
});
stack(ui, |ui| {
small(ui, app, "sources", Some("Manage Sources"), "Manage Sources", "references.sources", json!({}), false);
small(
ui,
app,
"sources",
Some("Manage Sources"),
"Manage Sources",
crate::widgets::ButtonAction::new("references.sources", json!({}), false),
);
let current = app.session.bib_style.clone();
let label = crate::i18n::fmt(tl!("Style: {style}"), &[("style", &current)]);
menu_button(ui, app, "styles", Some(&label), "Citation Style", false, "references.citationStyle", |ui, app| {
for st in &wordcraft_engine::cmd::citations::STYLES {
mi_check(ui, app, st.name, st.name == current, "references.citationStyle", json!({"style": st.name}));
}
});
small(ui, app, "bibliography", Some("Bibliography"), "Bibliography", "references.bibliography", json!({}), false);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("styles", Some(&label), "Citation Style", false, "references.citationStyle"),
|ui, app| {
for st in &wordcraft_engine::cmd::citations::STYLES {
mi_check(ui, app, st.name, st.name == current, "references.citationStyle", json!({"style": st.name}));
}
},
);
small(
ui,
app,
"bibliography",
Some("Bibliography"),
"Bibliography",
crate::widgets::ButtonAction::new("references.bibliography", json!({}), false),
);
});
});
group(ui, "Captions", None, app, |ui, app| {
@@ -66,19 +116,38 @@ pub(super) fn references(app: &mut WordApp, ui: &mut Ui) {
"tableOfFigures",
Some("Insert Table of Figures"),
"Insert Table of Figures",
"references.tableOfFigures",
json!({}),
false,
crate::widgets::ButtonAction::new("references.tableOfFigures", json!({}), false),
);
small(
ui,
app,
"update",
Some("Update Table"),
"Update Table of Figures",
crate::widgets::ButtonAction::new("references.updateFigures", json!({}), false),
);
small(
ui,
app,
"crossRef",
Some("Cross-reference"),
"Cross-reference",
crate::widgets::ButtonAction::new("insert.crossReference", json!({}), false),
);
small(ui, app, "update", Some("Update Table"), "Update Table of Figures", "references.updateFigures", json!({}), false);
small(ui, app, "crossRef", Some("Cross-reference"), "Cross-reference", "insert.crossReference", json!({}), false);
});
});
group(ui, "Index", None, app, |ui, app| {
big(ui, app, "markEntry", "Mark\nEntry", "references.markEntry", json!({}), false);
stack(ui, |ui| {
small(ui, app, "index", Some("Insert Index"), "Insert Index", "references.index", json!({}), false);
small(ui, app, "update", Some("Update Index"), "Update Index", "references.updateIndex", json!({}), false);
small(ui, app, "index", Some("Insert Index"), "Insert Index", crate::widgets::ButtonAction::new("references.index", json!({}), false));
small(
ui,
app,
"update",
Some("Update Index"),
"Update Index",
crate::widgets::ButtonAction::new("references.updateIndex", json!({}), false),
);
});
});
group(ui, "Table of Authorities", None, app, |ui, app| {
@@ -90,11 +159,16 @@ pub(super) fn references(app: &mut WordApp, ui: &mut Ui) {
"tableOfAuthorities",
Some("Insert Table of Authorities"),
"Insert Table of Authorities",
"references.tableOfAuthorities",
json!({}),
false,
crate::widgets::ButtonAction::new("references.tableOfAuthorities", json!({}), false),
);
small(
ui,
app,
"update",
Some("Update Table"),
"Update Table",
crate::widgets::ButtonAction::new("references.updateAuthorities", json!({}), false),
);
small(ui, app, "update", Some("Update Table"), "Update Table", "references.updateAuthorities", json!({}), false);
});
});
}
+71 -20
View File
@@ -1,13 +1,20 @@
use super::common::{mi, stack};
use super::language::language_menu;
use super::*;
use super::{Ui, WordApp, big, big_toggle, group, json, menu_button, small};
pub(super) fn review(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Proofing", None, app, |ui, app| {
big(ui, app, "spelling", "Spelling &\nGrammar", "review.spelling", json!({}), false);
stack(ui, |ui| {
small(ui, app, "thesaurus", Some("Thesaurus"), "Thesaurus", "review.thesaurus", json!({}), false);
small(ui, app, "wordCount", Some("Word Count"), "Word Count", "ui.dialog", json!({"name": "wordCount"}), false);
small(ui, app, "thesaurus", Some("Thesaurus"), "Thesaurus", crate::widgets::ButtonAction::new("review.thesaurus", json!({}), false));
small(
ui,
app,
"wordCount",
Some("Word Count"),
"Word Count",
crate::widgets::ButtonAction::new("ui.dialog", json!({"name": "wordCount"}), false),
);
});
});
group(ui, "Speech", None, app, |ui, app| {
@@ -18,14 +25,28 @@ pub(super) fn review(app: &mut WordApp, ui: &mut Ui) {
});
group(ui, "Language", None, app, |ui, app| {
big(ui, app, "translate", "Translate", "review.translate", json!({}), false);
menu_button(ui, app, "language", Some("Language"), "Language", true, "", language_menu);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("language", Some("Language"), "Language", true, ""), language_menu);
});
group(ui, "Comments", None, app, |ui, app| {
big(ui, app, "newComment", "New\nComment", "review.newComment", json!({}), false);
stack(ui, |ui| {
small(ui, app, "deleteComment", Some("Delete"), "Delete Comment", "review.deleteComment", json!({}), false);
small(ui, app, "prevComment", Some("Previous"), "Previous Comment", "review.previousComment", json!({}), false);
small(ui, app, "nextComment", Some("Next"), "Next Comment", "review.nextComment", json!({}), false);
small(
ui,
app,
"deleteComment",
Some("Delete"),
"Delete Comment",
crate::widgets::ButtonAction::new("review.deleteComment", json!({}), false),
);
small(
ui,
app,
"prevComment",
Some("Previous"),
"Previous Comment",
crate::widgets::ButtonAction::new("review.previousComment", json!({}), false),
);
small(ui, app, "nextComment", Some("Next"), "Next Comment", crate::widgets::ButtonAction::new("review.nextComment", json!({}), false));
});
let shown = app.session.view.comments_pane;
big(ui, app, "showComments", "Show\nComments", "view.commentsPane", json!({"value": !shown}), false);
@@ -38,35 +59,51 @@ pub(super) fn review(app: &mut WordApp, ui: &mut Ui) {
menu_button(
ui,
app,
"markup",
Some(if app.session.view.show_markup { "All Markup" } else { "No Markup" }),
"Display for Review",
false,
"review.markup",
crate::widgets::MenuButtonOptions::new(
"markup",
Some(if app.session.view.show_markup { "All Markup" } else { "No Markup" }),
"Display for Review",
false,
"review.markup",
),
|ui, app| {
mi(ui, app, "All Markup", "review.markup", json!({"value": "all"}));
mi(ui, app, "No Markup", "review.markup", json!({"value": "noMarkup"}));
},
);
small(ui, app, "reviewingPane", Some("Reviewing Pane"), "Reviewing Pane", "review.changes", json!({}), false);
small(
ui,
app,
"reviewingPane",
Some("Reviewing Pane"),
"Reviewing Pane",
crate::widgets::ButtonAction::new("review.changes", json!({}), false),
);
});
});
group(ui, "Changes", None, app, |ui, app| {
menu_button(ui, app, "accept", Some("Accept"), "Accept", true, "review.accept", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("accept", Some("Accept"), "Accept", true, "review.accept"), |ui, app| {
mi(ui, app, "Accept and Move to Next", "review.accept", json!({"next": true}));
mi(ui, app, "Accept This Change", "review.accept", json!({}));
mi(ui, app, "Accept All Changes", "review.acceptAll", json!({}));
mi(ui, app, "Accept All Changes and Stop Tracking", "review.acceptAll", json!({"stopTracking": true}));
});
menu_button(ui, app, "reject", Some("Reject"), "Reject", true, "review.reject", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("reject", Some("Reject"), "Reject", true, "review.reject"), |ui, app| {
mi(ui, app, "Reject and Move to Next", "review.reject", json!({"next": true}));
mi(ui, app, "Reject Change", "review.reject", json!({}));
mi(ui, app, "Reject All Changes", "review.rejectAll", json!({}));
mi(ui, app, "Reject All Changes and Stop Tracking", "review.rejectAll", json!({"stopTracking": true}));
});
stack(ui, |ui| {
small(ui, app, "prevChange", Some("Previous"), "Previous Change", "review.previousChange", json!({}), false);
small(ui, app, "nextChange", Some("Next"), "Next Change", "review.nextChange", json!({}), false);
small(
ui,
app,
"prevChange",
Some("Previous"),
"Previous Change",
crate::widgets::ButtonAction::new("review.previousChange", json!({}), false),
);
small(ui, app, "nextChange", Some("Next"), "Next Change", crate::widgets::ButtonAction::new("review.nextChange", json!({}), false));
});
});
group(ui, "Compare", None, app, |ui, app| {
@@ -108,14 +145,28 @@ pub(super) fn developer(app: &mut WordApp, ui: &mut Ui) {
for row in rows {
crate::widgets::row(ui, |ui| {
for (icon, tip, id) in row {
small(ui, app, icon, None, tip, id, json!({}), false);
small(ui, app, icon, None, tip, crate::widgets::ButtonAction::new(id, json!({}), false));
}
});
}
});
stack(ui, |ui| {
small(ui, app, "designMode", Some("Design Mode"), "Design Mode", "developer.designMode", json!({}), design);
small(ui, app, "properties", Some("Properties"), "Properties", "developer.properties", json!({}), false);
small(
ui,
app,
"designMode",
Some("Design Mode"),
"Design Mode",
crate::widgets::ButtonAction::new("developer.designMode", json!({}), design),
);
small(
ui,
app,
"properties",
Some("Properties"),
"Properties",
crate::widgets::ButtonAction::new("developer.properties", json!({}), false),
);
});
});
}
+78 -41
View File
@@ -1,7 +1,7 @@
use super::arrange::{align_menu, rotate_menu, rotation_field, wrap_menu};
use super::common::{mi, stack};
use super::language::mi_check;
use super::*;
use super::{Target, Ui, WordApp, big, group, json, menu_button, small, vec2};
/// Shape Fill › Gradient: No Gradient, Light and Dark Variations of the shape's colour, More
/// Gradients… (the Format Shape pane).
@@ -53,32 +53,45 @@ pub(super) fn shape_format(app: &mut WordApp, ui: &mut Ui) {
let theme = app.session.doc.settings.theme_colors.clone();
group(ui, "Shape Styles", None, app, |ui, app| {
stack(ui, |ui| {
menu_button(ui, app, "shading", Some("Shape Fill"), "Shape Fill", false, "shape.fill", |ui, app| {
mi(ui, app, "No Color", "shape.fill", json!({"color": null}));
crate::colors::menu(ui, app, &theme, Target::cmd("shape.fill"));
ui.separator();
ui.menu_button(tl!("Gradient"), |ui| gradient_menu(ui, app, fill.as_ref()));
});
menu_button(ui, app, "pen", Some("Shape Outline"), "Shape Outline", false, "shape.outline", |ui, app| {
mi(ui, app, "No Color", "shape.outline", json!({"color": null}));
crate::colors::menu(ui, app, &theme, Target::cmd("shape.outline"));
ui.separator();
ui.menu_button(tl!("Weight"), |ui| {
let color = stroke.unwrap_or(wordcraft_doc::Rgb::BLACK).hex();
for w in [0.25, 0.5, 0.75, 1.0, 1.5, 2.25, 3.0, 4.5, 6.0] {
mi(ui, app, &format!("{w} pt"), "shape.outline", json!({"color": color, "width": w}));
}
});
});
menu_button(ui, app, "shapeEffects", Some("Shape Effects"), "Shadow, glow and soft edges", false, "shape.effects", |ui, app| {
shape_effects_menu(ui, app, &theme, glow_size)
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("shading", Some("Shape Fill"), "Shape Fill", false, "shape.fill"),
|ui, app| {
mi(ui, app, "No Color", "shape.fill", json!({"color": null}));
crate::colors::menu(ui, app, &theme, Target::cmd("shape.fill"));
ui.separator();
ui.menu_button(tl!("Gradient"), |ui| gradient_menu(ui, app, fill.as_ref()));
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("pen", Some("Shape Outline"), "Shape Outline", false, "shape.outline"),
|ui, app| {
mi(ui, app, "No Color", "shape.outline", json!({"color": null}));
crate::colors::menu(ui, app, &theme, Target::cmd("shape.outline"));
ui.separator();
ui.menu_button(tl!("Weight"), |ui| {
let color = stroke.unwrap_or(wordcraft_doc::Rgb::BLACK).hex();
for w in [0.25, 0.5, 0.75, 1.0, 1.5, 2.25, 3.0, 4.5, 6.0] {
mi(ui, app, &format!("{w} pt"), "shape.outline", json!({"color": color, "width": w}));
}
});
},
);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("shapeEffects", Some("Shape Effects"), "Shadow, glow and soft edges", false, "shape.effects"),
|ui, app| shape_effects_menu(ui, app, &theme, glow_size),
);
});
});
alt_text_group(ui, app);
if wordcraft_engine::cmd::canvas::selected_canvas(&app.session).is_some() {
group(ui, "Drawing Canvas", None, app, |ui, app| {
menu_button(ui, app, "canvas", Some("Drawing\nCanvas"), "Drawing Canvas", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("canvas", Some("Drawing\nCanvas"), "Drawing Canvas", true, ""), |ui, app| {
mi(ui, app, "Fit", "canvas.fit", json!({}));
mi(ui, app, "Expand", "canvas.expand", json!({}));
ui.menu_button(tl!("Scale Drawing"), |ui| {
@@ -90,16 +103,21 @@ pub(super) fn shape_format(app: &mut WordApp, ui: &mut Ui) {
});
}
group(ui, "WordArt Styles", None, app, |ui, app| {
menu_button(ui, app, "wordArt", Some("Styles"), "WordArt Styles", true, "", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("wordArt", Some("Styles"), "WordArt Styles", true, ""), |ui, app| {
crate::wordart_menus::style_gallery(ui, app, "wordArt.style")
});
stack(ui, |ui| {
menu_button(ui, app, "textFill", Some("Text Fill"), "Text Fill", false, "", |ui, app| crate::wordart_menus::fill_menu(ui, app, &theme));
let outline = crate::wordart_menus::current_outline(app);
menu_button(ui, app, "textOutline", Some("Text Outline"), "Text Outline", false, "", |ui, app| {
crate::wordart_menus::outline_menu(ui, app, &theme, outline)
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("textFill", Some("Text Fill"), "Text Fill", false, ""), |ui, app| {
crate::wordart_menus::fill_menu(ui, app, &theme)
});
menu_button(ui, app, "transform", Some("Text Effects"), "Text Effects", false, "", |ui, app| {
let outline = crate::wordart_menus::current_outline(app);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("textOutline", Some("Text Outline"), "Text Outline", false, ""),
|ui, app| crate::wordart_menus::outline_menu(ui, app, &theme, outline),
);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("transform", Some("Text Effects"), "Text Effects", false, ""), |ui, app| {
crate::wordart_menus::effects_menu(ui, app, &theme, true)
});
});
@@ -146,26 +164,45 @@ fn shape_text_group(app: &mut WordApp, ui: &mut Ui) {
let body = target.and_then(|(_, id)| app.session.doc.parts.get(&id).map(|p| p.body)).unwrap_or_default();
group(ui, "Text", None, app, |ui, app| {
stack(ui, |ui| {
menu_button(ui, app, "textDirection", Some("Text Direction"), "Text Direction", false, "", |ui, app| {
for (label, vert) in [
("Horizontal", TextVert::Horz),
("Rotate all text 90°", TextVert::Vert),
("Rotate all text 270°", TextVert::Vert270),
("Stacked", TextVert::Stacked),
] {
mi_check(ui, app, label, body.vert == vert, "shape.textDirection", json!({"dir": vert.ooxml()}));
}
});
menu_button(ui, app, "alignText", Some("Align Text"), "Align Text", false, "", |ui, app| {
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("textDirection", Some("Text Direction"), "Text Direction", false, ""),
|ui, app| {
for (label, vert) in [
("Horizontal", TextVert::Horz),
("Rotate all text 90°", TextVert::Vert),
("Rotate all text 270°", TextVert::Vert270),
("Stacked", TextVert::Stacked),
] {
mi_check(ui, app, label, body.vert == vert, "shape.textDirection", json!({"dir": vert.ooxml()}));
}
},
);
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("alignText", Some("Align Text"), "Align Text", false, ""), |ui, app| {
for (label, anchor) in [("Top", VAlign::Top), ("Middle", VAlign::Center), ("Bottom", VAlign::Bottom)] {
mi_check(ui, app, label, body.anchor == anchor, "shape.alignText", json!({"anchor": wordcraft_doc::props::anchor_ooxml(anchor)}));
}
});
if body.next.is_some() {
small(ui, app, "textBoxUnlink", Some("Break Link"), "Break Link", "shape.breakLink", json!({}), false);
small(
ui,
app,
"textBoxUnlink",
Some("Break Link"),
"Break Link",
crate::widgets::ButtonAction::new("shape.breakLink", json!({}), false),
);
} else {
let waiting = app.canvas.link_from.is_some();
small(ui, app, "textBoxLink", Some("Create Link"), "Create Link", "ui.linkTextBox", json!({}), waiting);
small(
ui,
app,
"textBoxLink",
Some("Create Link"),
"Create Link",
crate::widgets::ButtonAction::new("ui.linkTextBox", json!({}), waiting),
);
}
});
});
+125 -46
View File
@@ -1,5 +1,5 @@
use super::common::{mi, stack};
use super::*;
use super::{MeasureExt, Remember, Target, Ui, WordApp, big, group, json, menu_button, small, split, vec2};
pub(super) fn table_design(app: &mut WordApp, ui: &mut Ui) {
let look = app.session.sel.focus.path.cell().and_then(|(tp, _, _)| app.session.doc.table(app.session.sel.focus.story, &tp).map(|t| t.props.look));
@@ -60,28 +60,41 @@ pub(super) fn table_design(app: &mut WordApp, ui: &mut Ui) {
let can_modify = current.is_some();
// Built-in table styles can't be deleted.
let can_delete = current == Some(false);
menu_button(ui, app, "styles", Some("Styles"), "Table Styles", false, "table.style", |ui, app| {
mi(ui, app, "New Table Style…", "ui.dialog", json!({"name": "newTableStyle"}));
if ui.add_enabled(can_modify, egui::Button::new(tl!("Modify Table Style…")).min_size(vec2(200.0, 0.0))).clicked() {
let _ = app.run("ui.dialog", json!({"name": "modifyTableStyle"}));
ui.close();
}
if ui.add_enabled(can_delete, egui::Button::new(tl!("Delete Table Style")).min_size(vec2(200.0, 0.0))).clicked() {
if let Err(e) = app.run("table.deleteStyle", json!({})) {
app.status(e);
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("styles", Some("Styles"), "Table Styles", false, "table.style"),
|ui, app| {
mi(ui, app, "New Table Style…", "ui.dialog", json!({"name": "newTableStyle"}));
if ui.add_enabled(can_modify, egui::Button::new(tl!("Modify Table Style…")).min_size(vec2(200.0, 0.0))).clicked() {
let _ = app.run("ui.dialog", json!({"name": "modifyTableStyle"}));
ui.close();
}
ui.close();
}
});
split(ui, app, "shading", "Shading", "table.shading", json!({"color": app.canvas.last_shading.clone()}), false, None, |ui, app| {
mi(ui, app, "No Color", "table.shading", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("table.shading").remember(Remember::Shading));
});
if ui.add_enabled(can_delete, egui::Button::new(tl!("Delete Table Style")).min_size(vec2(200.0, 0.0))).clicked() {
if let Err(e) = app.run("table.deleteStyle", json!({})) {
app.status(e);
}
ui.close();
}
},
);
split(
ui,
app,
"shading",
"Shading",
crate::widgets::ButtonAction::new("table.shading", json!({"color": app.canvas.last_shading.clone()}), false),
None,
|ui, app| {
mi(ui, app, "No Color", "table.shading", json!({"color": null}));
let theme = app.session.doc.settings.theme_colors.clone();
crate::colors::menu(ui, app, &theme, Target::cmd("table.shading").remember(Remember::Shading));
},
);
});
});
group(ui, "Borders", None, app, |ui, app| {
menu_button(ui, app, "borders", Some("Borders"), "Borders", true, "table.borders", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("borders", Some("Borders"), "Borders", true, "table.borders"), |ui, app| {
for (l, k) in [
("All Borders", "all"),
("Outside Borders", "outside"),
@@ -163,15 +176,34 @@ fn cell_size_boxes(ui: &mut Ui, app: &mut WordApp) {
pub(super) fn table_layout(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Table", None, app, |ui, app| {
stack(ui, |ui| {
menu_button(ui, app, "selectTable", Some("Select"), "Select", false, "table.selectTable", |ui, app| {
mi(ui, app, "Select Cell", "table.selectCell", json!({}));
mi(ui, app, "Select Column", "table.selectColumn", json!({}));
mi(ui, app, "Select Row", "table.selectRow", json!({}));
mi(ui, app, "Select Table", "table.selectTable", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("selectTable", Some("Select"), "Select", false, "table.selectTable"),
|ui, app| {
mi(ui, app, "Select Cell", "table.selectCell", json!({}));
mi(ui, app, "Select Column", "table.selectColumn", json!({}));
mi(ui, app, "Select Row", "table.selectRow", json!({}));
mi(ui, app, "Select Table", "table.selectTable", json!({}));
},
);
let g = app.session.view.table_gridlines;
small(ui, app, "gridlines", Some("View Gridlines"), "View Gridlines", "table.viewGridlines", json!({}), g);
small(ui, app, "properties", Some("Properties"), "Table Properties", "table.properties", json!({}), false);
small(
ui,
app,
"gridlines",
Some("View Gridlines"),
"View Gridlines",
crate::widgets::ButtonAction::new("table.viewGridlines", json!({}), g),
);
small(
ui,
app,
"properties",
Some("Properties"),
"Table Properties",
crate::widgets::ButtonAction::new("table.properties", json!({}), false),
);
});
});
group(ui, "Draw", None, app, |ui, app| {
@@ -181,36 +213,76 @@ pub(super) fn table_layout(app: &mut WordApp, ui: &mut Ui) {
crate::widgets::big_toggle(ui, app, "eraser", "Eraser", "table.eraser", tool == Some(TableTool::Erase));
});
group(ui, "Rows & Columns", None, app, |ui, app| {
menu_button(ui, app, "deleteTable", Some("Delete"), "Delete", true, "table.deleteTable", |ui, app| {
mi(ui, app, "Delete Cells", "table.deleteCells", json!({}));
mi(ui, app, "Delete Columns", "table.deleteColumn", json!({}));
mi(ui, app, "Delete Rows", "table.deleteRow", json!({}));
mi(ui, app, "Delete Table", "table.deleteTable", json!({}));
});
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("deleteTable", Some("Delete"), "Delete", true, "table.deleteTable"),
|ui, app| {
mi(ui, app, "Delete Cells", "table.deleteCells", json!({}));
mi(ui, app, "Delete Columns", "table.deleteColumn", json!({}));
mi(ui, app, "Delete Rows", "table.deleteRow", json!({}));
mi(ui, app, "Delete Table", "table.deleteTable", json!({}));
},
);
big(ui, app, "insertAbove", "Insert\nAbove", "table.insertRowAbove", json!({}), false);
stack(ui, |ui| {
small(ui, app, "insertBelow", Some("Insert Below"), "Insert Below", "table.insertRowBelow", json!({}), false);
small(ui, app, "insertLeft", Some("Insert Left"), "Insert Left", "table.insertColumnLeft", json!({}), false);
small(ui, app, "insertRight", Some("Insert Right"), "Insert Right", "table.insertColumnRight", json!({}), false);
small(
ui,
app,
"insertBelow",
Some("Insert Below"),
"Insert Below",
crate::widgets::ButtonAction::new("table.insertRowBelow", json!({}), false),
);
small(
ui,
app,
"insertLeft",
Some("Insert Left"),
"Insert Left",
crate::widgets::ButtonAction::new("table.insertColumnLeft", json!({}), false),
);
small(
ui,
app,
"insertRight",
Some("Insert Right"),
"Insert Right",
crate::widgets::ButtonAction::new("table.insertColumnRight", json!({}), false),
);
});
});
group(ui, "Merge", None, app, |ui, app| {
stack(ui, |ui| {
small(ui, app, "merge", Some("Merge Cells"), "Merge Cells", "table.merge", json!({}), false);
small(ui, app, "splitCells", Some("Split Cells"), "Split Cells", "table.split", json!({}), false);
small(ui, app, "splitTable", Some("Split Table"), "Split Table", "table.splitTable", json!({}), false);
small(ui, app, "merge", Some("Merge Cells"), "Merge Cells", crate::widgets::ButtonAction::new("table.merge", json!({}), false));
small(ui, app, "splitCells", Some("Split Cells"), "Split Cells", crate::widgets::ButtonAction::new("table.split", json!({}), false));
small(ui, app, "splitTable", Some("Split Table"), "Split Table", crate::widgets::ButtonAction::new("table.splitTable", json!({}), false));
});
});
group(ui, "Cell Size", None, app, |ui, app| {
menu_button(ui, app, "autofit", Some("AutoFit"), "AutoFit", true, "table.autofit", |ui, app| {
menu_button(ui, app, crate::widgets::MenuButtonOptions::new("autofit", Some("AutoFit"), "AutoFit", true, "table.autofit"), |ui, app| {
mi(ui, app, "AutoFit Contents", "table.autofit", json!({"mode": "contents"}));
mi(ui, app, "AutoFit Window", "table.autofit", json!({"mode": "window"}));
mi(ui, app, "Fixed Column Width", "table.autofit", json!({"mode": "fixed"}));
});
cell_size_boxes(ui, app);
stack(ui, |ui| {
small(ui, app, "distributeRows", Some("Distribute Rows"), "Distribute Rows", "table.distributeRows", json!({}), false);
small(ui, app, "distributeCols", Some("Distribute Columns"), "Distribute Columns", "table.distributeColumns", json!({}), false);
small(
ui,
app,
"distributeRows",
Some("Distribute Rows"),
"Distribute Rows",
crate::widgets::ButtonAction::new("table.distributeRows", json!({}), false),
);
small(
ui,
app,
"distributeCols",
Some("Distribute Columns"),
"Distribute Columns",
crate::widgets::ButtonAction::new("table.distributeColumns", json!({}), false),
);
});
});
group(ui, "Alignment", None, app, |ui, app| {
@@ -221,7 +293,7 @@ pub(super) fn table_layout(app: &mut WordApp, ui: &mut Ui) {
[("bottomLeft", "Align Bottom Left"), ("bottomCenter", "Align Bottom Center"), ("bottomRight", "Align Bottom Right")],
] {
for (v, tip) in row {
small(ui, app, "cellAlign", None, tip, "table.cellAlign", json!({"value": v}), false);
small(ui, app, "cellAlign", None, tip, crate::widgets::ButtonAction::new("table.cellAlign", json!({"value": v}), false));
}
ui.end_row();
}
@@ -232,9 +304,16 @@ pub(super) fn table_layout(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Data", None, app, |ui, app| {
big(ui, app, "sort", "Sort", "table.sort", json!({}), false);
stack(ui, |ui| {
small(ui, app, "repeatHeader", Some("Repeat Header Rows"), "Repeat Header Rows", "table.repeatHeader", json!({}), false);
small(ui, app, "toText", Some("Convert to Text"), "Convert to Text", "table.toText", json!({}), false);
small(ui, app, "formula", Some("Formula"), "Formula", "table.formula", json!({}), false);
small(
ui,
app,
"repeatHeader",
Some("Repeat Header Rows"),
"Repeat Header Rows",
crate::widgets::ButtonAction::new("table.repeatHeader", json!({}), false),
);
small(ui, app, "toText", Some("Convert to Text"), "Convert to Text", crate::widgets::ButtonAction::new("table.toText", json!({}), false));
small(ui, app, "formula", Some("Formula"), "Formula", crate::widgets::ButtonAction::new("table.formula", json!({}), false));
});
});
}
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Stroke, Tokens, Ui, WordApp, json, semibold};
pub const TABS: [&str; 13] = wordcraft_engine::ribbon::TABS;
+1 -1
View File
@@ -1,5 +1,5 @@
use super::common::mi;
use super::*;
use super::{Rect, Stroke, Target, Tokens, Ui, WordApp, json, pos2, vec2};
/// The underline styles Home › Underline offers as line samples: (`format.underline` style, name).
pub(super) const UNDERLINE_STYLES: [(&str, &str); 8] = [
+57 -28
View File
@@ -1,5 +1,5 @@
use super::common::stack;
use super::*;
use super::{Ui, WordApp, big, big_toggle, group, json, menu_button, small, vec2};
/// Draws the View tab's Views, Immersive, Page Movement, Show, Zoom, Dark Mode, Window and Macros
/// and Templates groups.
@@ -10,8 +10,8 @@ pub(super) fn view(app: &mut WordApp, ui: &mut Ui) {
big(ui, app, "printLayout", "Print\nLayout", "view.printLayout", json!({}), false);
big(ui, app, "webLayout", "Web\nLayout", "view.webLayout", json!({}), false);
stack(ui, |ui| {
small(ui, app, "outline", Some("Outline"), "Outline", "view.outline", json!({}), false);
small(ui, app, "draft", Some("Draft"), "Draft", "view.draft", json!({}), false);
small(ui, app, "outline", Some("Outline"), "Outline", crate::widgets::ButtonAction::new("view.outline", json!({}), false));
small(ui, app, "draft", Some("Draft"), "Draft", crate::widgets::ButtonAction::new("view.draft", json!({}), false));
});
});
group(ui, "Immersive", None, app, |ui, app| {
@@ -50,25 +50,44 @@ pub(super) fn view(app: &mut WordApp, ui: &mut Ui) {
big(ui, app, "zoom100", "100%", "view.zoom100", json!({}), false);
// Step the zoom up and down by 10% (issue #67), from whatever the page shows now.
stack(ui, |ui| {
small(ui, app, "zoomIn", Some("Zoom In"), "Zoom In", "view.zoomIn", json!({}), false);
small(ui, app, "zoomOut", Some("Zoom Out"), "Zoom Out", "view.zoomOut", json!({}), false);
small(ui, app, "zoomIn", Some("Zoom In"), "Zoom In", crate::widgets::ButtonAction::new("view.zoomIn", json!({}), false));
small(ui, app, "zoomOut", Some("Zoom Out"), "Zoom Out", crate::widgets::ButtonAction::new("view.zoomOut", json!({}), false));
});
stack(ui, |ui| {
small(ui, app, "onePage", Some("One Page"), "One Page", "view.onePage", json!({}), v.fit == "onePage");
small(ui, app, "multiplePages", Some("Multiple Pages"), "Multiple Pages", "view.multiplePages", json!({}), v.multi_page);
small(ui, app, "pageWidth", Some("Page Width"), "Page Width", "view.pageWidth", json!({}), v.fit == "pageWidth");
small(ui, app, "onePage", Some("One Page"), "One Page", crate::widgets::ButtonAction::new("view.onePage", json!({}), v.fit == "onePage"));
small(
ui,
app,
"multiplePages",
Some("Multiple Pages"),
"Multiple Pages",
crate::widgets::ButtonAction::new("view.multiplePages", json!({}), v.multi_page),
);
small(
ui,
app,
"pageWidth",
Some("Page Width"),
"Page Width",
crate::widgets::ButtonAction::new("view.pageWidth", json!({}), v.fit == "pageWidth"),
);
});
});
group(ui, "Dark Mode", None, app, |ui, app| {
big(ui, app, "darkMode", "Switch\nModes", "view.darkMode", json!({}), false);
menu_button(ui, app, "interfaceTheme", Some("Interface\nTheme"), "Interface Theme", true, "", |ui, app| {
for a in crate::theme::Appearance::ALL {
if ui.add(egui::Button::selectable(app.ui.theme == a, tl!(a.label())).min_size(vec2(200.0, 0.0))).clicked() {
let _ = app.run("view.interfaceTheme", json!({"value": a.code()}));
ui.close();
menu_button(
ui,
app,
crate::widgets::MenuButtonOptions::new("interfaceTheme", Some("Interface\nTheme"), "Interface Theme", true, ""),
|ui, app| {
for a in crate::theme::Appearance::ALL {
if ui.add(egui::Button::selectable(app.ui.theme == a, tl!(a.label())).min_size(vec2(200.0, 0.0))).clicked() {
let _ = app.run("view.interfaceTheme", json!({"value": a.code()}));
ui.close();
}
}
}
});
},
);
});
group(ui, "Window", None, app, |ui, app| {
big(ui, app, "newWindow", "New\nWindow", "view.newWindow", json!({}), false);
@@ -77,11 +96,7 @@ pub(super) fn view(app: &mut WordApp, ui: &mut Ui) {
menu_button(
ui,
app,
"switchWindows",
Some("Switch\nWindows"),
"Switch Windows",
true,
"view.switchWindows",
crate::widgets::MenuButtonOptions::new("switchWindows", Some("Switch\nWindows"), "Switch Windows", true, "view.switchWindows"),
crate::documents::switch_windows_menu,
);
// The Full Screen button carries its name for assistive technology (and for the
@@ -110,8 +125,15 @@ pub(super) fn outlining(app: &mut WordApp, ui: &mut Ui) {
group(ui, "Outline Tools", None, app, |ui, app| {
stack(ui, |ui| {
crate::widgets::row(ui, |ui| {
small(ui, app, "outlineTop", None, "Promote to Heading 1", "outline.promoteToHeading1", json!({}), false);
small(ui, app, "outlinePromote", None, "Promote", "outline.promote", json!({}), false);
small(
ui,
app,
"outlineTop",
None,
"Promote to Heading 1",
crate::widgets::ButtonAction::new("outline.promoteToHeading1", json!({}), false),
);
small(ui, app, "outlinePromote", None, "Promote", crate::widgets::ButtonAction::new("outline.promote", json!({}), false));
let levels: Vec<String> = (1..=BODY).map(name).collect();
if let Some(v) = crate::widgets::combo_hint(ui, "outlineLevel", 100.0, &name(level), &levels, None, tl!("Outline Level"))
&& let Some(i) = levels.iter().position(|x| *x == v)
@@ -119,15 +141,22 @@ pub(super) fn outlining(app: &mut WordApp, ui: &mut Ui) {
let l = if i + 1 >= usize::from(BODY) { json!("body") } else { json!(i + 1) };
let _ = app.run("outline.level", json!({ "level": l }));
}
small(ui, app, "outlineDemote", None, "Demote", "outline.demote", json!({}), false);
small(ui, app, "outlineBody", None, "Demote to Body Text", "outline.demoteToBody", json!({}), false);
small(ui, app, "outlineDemote", None, "Demote", crate::widgets::ButtonAction::new("outline.demote", json!({}), false));
small(
ui,
app,
"outlineBody",
None,
"Demote to Body Text",
crate::widgets::ButtonAction::new("outline.demoteToBody", json!({}), false),
);
});
ui.add_space(3.0);
crate::widgets::row(ui, |ui| {
small(ui, app, "outlineUp", None, "Move Up", "outline.moveUp", json!({}), false);
small(ui, app, "outlineDown", None, "Move Down", "outline.moveDown", json!({}), false);
small(ui, app, "outlineExpand", None, "Expand", "outline.expand", json!({}), false);
small(ui, app, "outlineCollapse", None, "Collapse", "outline.collapse", json!({}), false);
small(ui, app, "outlineUp", None, "Move Up", crate::widgets::ButtonAction::new("outline.moveUp", json!({}), false));
small(ui, app, "outlineDown", None, "Move Down", crate::widgets::ButtonAction::new("outline.moveDown", json!({}), false));
small(ui, app, "outlineExpand", None, "Expand", crate::widgets::ButtonAction::new("outline.expand", json!({}), false));
small(ui, app, "outlineCollapse", None, "Collapse", crate::widgets::ButtonAction::new("outline.collapse", json!({}), false));
ui.add_space(6.0);
ui.label(egui::RichText::new(tl!("Show Level")).size(11.5));
let mut shown: Vec<String> = vec![tl!("All Levels").to_string()];