mirror of
https://github.com/aphrody-labs/wordcraft.git
synced 2026-10-14 03:29:04 +00:00
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
4 files changed
+48
-20
No files matched your search
@@ -62,8 +62,7 @@ fn math_menu_button(ui: &mut Ui, app: &mut WordApp, picture: &str, label: &str,
|
||||
let lines: Vec<&str> = label.split('\n').collect();
|
||||
let mut y = r.min.y + 43.0;
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
let txt = if i + 1 == lines.len() { format!("{l} ▾") } else { (*l).to_string() };
|
||||
ui.painter().text(pos2(r.center().x, y), Align2::CENTER_TOP, txt, regular(11.5), t.text);
|
||||
crate::widgets::label_line(ui, r.center().x, y, l, regular(11.5), t.text, i + 1 == lines.len());
|
||||
y += 13.0;
|
||||
}
|
||||
let resp = resp.on_hover_text(label.replace('\n', " "));
|
||||
@@ -163,7 +162,7 @@ pub fn insert_button(ui: &mut Ui, app: &mut WordApp) {
|
||||
}
|
||||
}
|
||||
crate::icons::paint(ui.painter(), Rect::from_center_size(pos2(r.center().x, r.min.y + 20.0), vec2(32.0, 32.0)), "equation", t.icon, t.accent);
|
||||
ui.painter().text(pos2(r.center().x, r.min.y + 43.0), Align2::CENTER_TOP, format!("{label} ▾"), regular(11.5), t.text);
|
||||
crate::widgets::label_line(ui, r.center().x, r.min.y + 43.0, label, regular(11.5), t.text, true);
|
||||
if rt.on_hover_text(tl!("Insert a new equation (Alt+=)")).clicked() {
|
||||
let _ = app.run("insert.equation", json!({}));
|
||||
app.canvas.want_focus = true;
|
||||
|
||||
@@ -172,12 +172,8 @@ pub fn show(app: &mut WordApp, ui: &mut Ui) {
|
||||
}
|
||||
ui.add_space(6.0);
|
||||
let track = app.session.doc.settings.track_changes;
|
||||
let mode = crate::widgets::icon_text_button(
|
||||
ui,
|
||||
"pencil",
|
||||
&format!("{} ▾", if track { tl!("Reviewing") } else { tl!("Editing") }),
|
||||
regular(12.0),
|
||||
);
|
||||
let mode =
|
||||
crate::widgets::icon_text_menu_button(ui, "pencil", if track { tl!("Reviewing") } else { tl!("Editing") }, regular(12.0));
|
||||
egui::Popup::menu(&mode).show(|ui| {
|
||||
if ui.selectable_label(!track, tl!(tl!("Editing — edit the document directly"))).clicked() {
|
||||
let _ = app.run("review.trackChanges", json!({"value": false}));
|
||||
|
||||
@@ -125,6 +125,24 @@ pub fn big_toggle(ui: &mut Ui, app: &mut WordApp, icon: &str, label: &str, id: &
|
||||
big_button(ui, app, icon, label, id, Value::Object(Default::default()), false, checked)
|
||||
}
|
||||
|
||||
/// Size of the drop-down marker drawn after a label or beside a split button (#659: one marker,
|
||||
/// the `dropdown` icon, everywhere; no text glyph).
|
||||
pub const DROPDOWN: f32 = 10.0;
|
||||
|
||||
/// One centred label line (top at `y`); `marker` draws the drop-down icon after the text, in the
|
||||
/// text's colour, and the pair is centred together.
|
||||
pub fn label_line(ui: &Ui, cx: f32, y: f32, text: &str, font: egui::FontId, color: Color32, marker: bool) {
|
||||
let galley = ui.painter().layout_no_wrap(text.to_string(), font, color);
|
||||
let extra = if marker { DROPDOWN + 3.0 } else { 0.0 };
|
||||
let size = galley.size();
|
||||
let x = cx - (size.x + extra) / 2.0;
|
||||
ui.painter().galley(pos2(x, y), galley, color);
|
||||
if marker {
|
||||
let c = pos2(x + size.x + 3.0 + DROPDOWN / 2.0, y + size.y / 2.0);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(c, vec2(DROPDOWN, DROPDOWN)), "dropdown", color, color);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn big_button(ui: &mut Ui, app: &mut WordApp, icon: &str, label: &str, id: &str, params: Value, menu: bool, checked: bool) -> Response {
|
||||
let label = tl!(label);
|
||||
@@ -142,8 +160,7 @@ fn big_button(ui: &mut Ui, app: &mut WordApp, icon: &str, label: &str, id: &str,
|
||||
let mut y = r.min.y + 43.0;
|
||||
let lines: Vec<&str> = label.split('\n').collect();
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
let txt = if menu && i + 1 == lines.len() { format!("{l} ▾") } else { (*l).to_string() };
|
||||
ui.painter().text(pos2(r.center().x, y), Align2::CENTER_TOP, txt, regular(11.5), if on { t.text } else { t.text_disabled });
|
||||
label_line(ui, r.center().x, y, l, regular(11.5), if on { t.text } else { t.text_disabled }, menu && i + 1 == lines.len());
|
||||
y += 13.0;
|
||||
}
|
||||
let resp = tooltip(app, resp, &label.replace('\n', " "), id);
|
||||
@@ -202,7 +219,7 @@ pub fn split(
|
||||
if let Some(sw) = swatch {
|
||||
ui.painter().rect_filled(Rect::from_min_max(pos2(r.min.x + 4.0, r.max.y - 5.0), pos2(r.max.x - 4.0, r.max.y - 2.0)), 0.0, sw);
|
||||
}
|
||||
icons::paint(ui.painter(), Rect::from_center_size(ar.center(), vec2(10.0, 10.0)), "dropdown", t.icon, t.accent);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(ar.center(), vec2(DROPDOWN, DROPDOWN)), "dropdown", t.icon, t.accent);
|
||||
let resp = tooltip(app, resp, tip, id);
|
||||
command_menu(&resp, app, id);
|
||||
if resp.clicked() {
|
||||
@@ -214,7 +231,7 @@ pub fn split(
|
||||
});
|
||||
}
|
||||
|
||||
/// A button that only opens a menu (icon + label + ▾).
|
||||
/// A button that only opens a menu (icon + label + drop-down marker).
|
||||
pub fn menu_button(
|
||||
ui: &mut Ui,
|
||||
app: &mut WordApp,
|
||||
@@ -240,8 +257,7 @@ pub fn menu_button(
|
||||
let lines: Vec<&str> = label.unwrap_or("").split('\n').collect();
|
||||
let mut y = r.min.y + 43.0;
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
let txt = if i + 1 == lines.len() { format!("{l} ▾") } else { (*l).to_string() };
|
||||
ui.painter().text(pos2(r.center().x, y), Align2::CENTER_TOP, txt, regular(11.5), t.text);
|
||||
label_line(ui, r.center().x, y, l, regular(11.5), t.text, i + 1 == lines.len());
|
||||
y += 13.0;
|
||||
}
|
||||
resp
|
||||
@@ -254,7 +270,7 @@ pub fn menu_button(
|
||||
if let Some(l) = label {
|
||||
ui.painter().text(pos2(r.min.x + 24.0, r.center().y), Align2::LEFT_CENTER, l, regular(11.5), t.text);
|
||||
}
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.max.x - 6.0, r.center().y), vec2(9.0, 9.0)), "dropdown", t.icon, t.accent);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.max.x - 6.0, r.center().y), vec2(DROPDOWN, DROPDOWN)), "dropdown", t.icon, t.accent);
|
||||
resp
|
||||
};
|
||||
let resp = resp.on_hover_text(tip);
|
||||
@@ -575,7 +591,7 @@ fn combo_impl(
|
||||
}
|
||||
let (ar, aresp) = ui.allocate_exact_size(vec2(16.0, resp.rect.height()), Sense::click());
|
||||
ui.painter().rect(ar, 0.0, if aresp.hovered() { t.hover } else { t.input }, Stroke::new(1.0, t.input_border), egui::StrokeKind::Inside);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(ar.center(), vec2(10.0, 10.0)), "dropdown", t.icon, t.accent);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(ar.center(), vec2(DROPDOWN, DROPDOWN)), "dropdown", t.icon, t.accent);
|
||||
// The list drops from the box's left edge, as in Word, not from the arrow (#535).
|
||||
egui::Popup::menu(&aresp).anchor(resp.rect.union(ar)).show(|ui| {
|
||||
if let Some(v) = combo_list(ui, width, current, items, preview) {
|
||||
@@ -753,11 +769,21 @@ pub fn icon_button(ui: &mut Ui, icon: &str, tip: &str) -> Response {
|
||||
/// fonts and would draw as empty boxes, so icons are painted (`icons.rs`) rather than typed.
|
||||
/// It looks and reacts like `ui.button` (see [`drawn_state`]).
|
||||
pub fn icon_text_button(ui: &mut Ui, icon: &str, text: &str, font: egui::FontId) -> Response {
|
||||
icon_text_button_with(ui, icon, text, font, false)
|
||||
}
|
||||
|
||||
/// [`icon_text_button`] that opens a menu: the drop-down marker follows the text.
|
||||
pub fn icon_text_menu_button(ui: &mut Ui, icon: &str, text: &str, font: egui::FontId) -> Response {
|
||||
icon_text_button_with(ui, icon, text, font, true)
|
||||
}
|
||||
|
||||
fn icon_text_button_with(ui: &mut Ui, icon: &str, text: &str, font: egui::FontId, marker: bool) -> Response {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let galley = ui.painter().layout_no_wrap(text.to_string(), font, Color32::PLACEHOLDER);
|
||||
let pad = ui.spacing().button_padding;
|
||||
let icon_w = 16.0;
|
||||
let size = vec2(pad.x + icon_w + 4.0 + galley.size().x + pad.x, galley.size().y.max(icon_w) + 2.0 * pad.y);
|
||||
let marker_w = if marker { DROPDOWN + 4.0 } else { 0.0 };
|
||||
let size = vec2(pad.x + icon_w + 4.0 + galley.size().x + marker_w + pad.x, galley.size().y.max(icon_w) + 2.0 * pad.y);
|
||||
let (r, resp) = ui.allocate_exact_size(size, Sense::click());
|
||||
let st = drawn_state(ui, &resp, true);
|
||||
let r = r.expand(st.expansion);
|
||||
@@ -769,7 +795,13 @@ pub fn icon_text_button(ui: &mut Ui, icon: &str, text: &str, font: egui::FontId)
|
||||
t.icon,
|
||||
t.accent,
|
||||
);
|
||||
ui.painter().galley(pos2(r.min.x + pad.x + icon_w + 4.0, r.center().y - galley.size().y / 2.0), galley, st.text);
|
||||
let text_x = r.min.x + pad.x + icon_w + 4.0;
|
||||
let text_w = galley.size().x;
|
||||
ui.painter().galley(pos2(text_x, r.center().y - galley.size().y / 2.0), galley, st.text);
|
||||
if marker {
|
||||
let c = pos2(text_x + text_w + 4.0 + DROPDOWN / 2.0, r.center().y);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(c, vec2(DROPDOWN, DROPDOWN)), "dropdown", st.text, st.text);
|
||||
}
|
||||
resp
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
# UI parity with Microsoft Word
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (gallery previews dimmed in the dark theme, #663; previously trivial: hard-coded interface colours moved to the theme, #656; previously minor: status bar language follows the caret and sets the proofing language; Review › Language is a menu; previously trivial: Customize Ribbon and the Quick Access Toolbar, #379; previously: major, first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (one drop-down marker, #659; previously minor: status bar language follows the caret and sets the proofing language; Review › Language is a menu; previously trivial: Customize Ribbon and the Quick Access Toolbar, #379; previously: major, first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
|
||||
|
||||
How WordCraft looks and feels next to Word: ribbon, dialogs, panes, keyboard, mouse and touch.
|
||||
Typing behaviour has its own checklist ([`typing-parity.md`](typing-parity.md)). All assets are our
|
||||
@@ -69,6 +69,7 @@ own (`AGENTS.md`: no Microsoft iconography, ever).
|
||||
| 2026-10-11 | trivial | Text field and combo outlines reach 3:1 against their fill and surroundings in both themes (light #8A8A8A, dark #7A7A7A); a test pins the ratios (#657) |
|
||||
| 2026-10-11 | trivial | Font dialog: Asian text font and Latin text font boxes, each with its own list and type-to-find, shown for a Chinese/Japanese interface or East Asian text in the selection; OK sets only the fonts changed; `format.font {"eastAsia"}` (#505) |
|
||||
| 2026-10-11 | trivial | References › Table of Authorities: Insert Table of Authorities is labelled and joined by Update Table (`references.updateAuthorities`), as in the Index group (#661) |
|
||||
| 2026-10-11 | trivial | One drop-down marker everywhere: large ribbon buttons, Equation and the Editing/Reviewing button draw the `dropdown` icon after their label, like split and menu buttons, instead of a text glyph (#659) |
|
||||
| 2026-10-11 | trivial | Dark theme: Styles, style-set and table-style previews sit on a dimmer page tone; with View › Switch Modes on, the Styles gallery previews the dark page (#663) |
|
||||
| 2026-10-11 | trivial | Dialog primary buttons, the Discord pill, the table eraser line and the Navigation search highlight take their colours from the theme in both themes (`widgets::primary_button`); a test bans the old fixed values (#656) |
|
||||
| 2026-10-11 | trivial | Dark theme: Share, the avatar initials, the AutoSave knob and keytips use dark `on_accent` text on the light accent (6.2:1, 8.4:1 on hover; white was 2.9:1) (#655) |
|
||||
|
||||
Reference in new issue
Block a user