* #659: one drop-down marker (the dropdown icon) on every ribbon button Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #658: menu buttons grey out while their command is disabled Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #653: large-button labels end above the group title row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- 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:
parent
3c8840556a
commit
4a1acb6c3f
|
|
@ -11,7 +11,7 @@ use wordcraft_engine::math_gallery::{BUILT_INS, STRUCTURES, SYMBOL_SETS, Templat
|
|||
|
||||
use crate::WordApp;
|
||||
use crate::theme::{Tokens, regular, semibold};
|
||||
use crate::widgets::{CONTENT_H, group, small};
|
||||
use crate::widgets::{BIG_ICON_Y, BIG_LABEL_Y, CONTENT_H, LABEL_PITCH, group, small};
|
||||
|
||||
/// What each structure button shows.
|
||||
fn button_picture(id: &str) -> &'static str {
|
||||
|
|
@ -56,14 +56,14 @@ fn math_menu_button(ui: &mut Ui, app: &mut WordApp, picture: &str, label: &str,
|
|||
if resp.hovered() {
|
||||
ui.painter().rect_filled(r, 4.0, t.hover);
|
||||
}
|
||||
let pic = Rect::from_center_size(pos2(r.center().x, r.min.y + 20.0), vec2(40.0, 34.0));
|
||||
let pic = Rect::from_center_size(pos2(r.center().x, r.min.y + BIG_ICON_Y), vec2(40.0, 32.0));
|
||||
ui.painter().rect_filled(pic, 3.0, egui::Color32::WHITE);
|
||||
paint_math(app, ui, pic, &format!("btn:{picture}"), picture, None, false, 12.0);
|
||||
let lines: Vec<&str> = label.split('\n').collect();
|
||||
let mut y = r.min.y + 43.0;
|
||||
let mut y = r.min.y + BIG_LABEL_Y;
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
crate::widgets::label_line(ui, r.center().x, y, l, regular(11.5), t.text, i + 1 == lines.len());
|
||||
y += 13.0;
|
||||
y += LABEL_PITCH;
|
||||
}
|
||||
let resp = resp.on_hover_text(label.replace('\n', " "));
|
||||
egui::Popup::menu(&resp).show(|ui| {
|
||||
|
|
@ -152,8 +152,8 @@ pub fn insert_button(ui: &mut Ui, app: &mut WordApp) {
|
|||
let label = tl!("Equation");
|
||||
let w = (ui.ctx().fonts_mut(|f| f.layout_no_wrap(label.to_string(), regular(11.5), t.text).size().x) + 22.0).max(48.0);
|
||||
let (r, _) = ui.allocate_exact_size(vec2(w, CONTENT_H), Sense::hover());
|
||||
let top = Rect::from_min_max(r.min, pos2(r.max.x, r.min.y + 40.0));
|
||||
let bottom = Rect::from_min_max(pos2(r.min.x, r.min.y + 40.0), r.max);
|
||||
let top = Rect::from_min_max(r.min, pos2(r.max.x, r.min.y + BIG_LABEL_Y - 2.0));
|
||||
let bottom = Rect::from_min_max(pos2(r.min.x, r.min.y + BIG_LABEL_Y - 2.0), r.max);
|
||||
let rt = ui.interact(top, ui.id().with("eq_insert_top"), Sense::click());
|
||||
let rb = ui.interact(bottom, ui.id().with("eq_insert_bottom"), Sense::click());
|
||||
for (rr, resp) in [(top, &rt), (bottom, &rb)] {
|
||||
|
|
@ -161,8 +161,14 @@ pub fn insert_button(ui: &mut Ui, app: &mut WordApp) {
|
|||
ui.painter().rect_filled(rr, 4.0, t.hover);
|
||||
}
|
||||
}
|
||||
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);
|
||||
crate::widgets::label_line(ui, r.center().x, r.min.y + 43.0, label, regular(11.5), t.text, true);
|
||||
crate::icons::paint(
|
||||
ui.painter(),
|
||||
Rect::from_center_size(pos2(r.center().x, r.min.y + BIG_ICON_Y), vec2(32.0, 32.0)),
|
||||
"equation",
|
||||
t.icon,
|
||||
t.accent,
|
||||
);
|
||||
crate::widgets::label_line(ui, r.center().x, r.min.y + BIG_LABEL_Y, 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;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,17 @@ use crate::{WordApp, icons};
|
|||
|
||||
/// Height of the ribbon content area (without group labels).
|
||||
pub const CONTENT_H: f32 = 66.0;
|
||||
pub const LABEL_H: f32 = 16.0;
|
||||
/// Height of the group title row under the content.
|
||||
pub const LABEL_H: f32 = 18.0;
|
||||
/// Large buttons: centre of the 32 px icon, top of the first label line, and the pitch and
|
||||
/// height of a label line (`regular(11.5)`; a test checks the font's real line height).
|
||||
pub const BIG_ICON_Y: f32 = 18.0;
|
||||
pub const BIG_LABEL_Y: f32 = 36.0;
|
||||
pub const LABEL_PITCH: f32 = 13.0;
|
||||
pub const LABEL_LINE_H: f32 = 14.0;
|
||||
// #653: a two-line label ends inside the content, above the group title row.
|
||||
const _: () = assert!(BIG_LABEL_Y + LABEL_PITCH + LABEL_LINE_H <= CONTENT_H);
|
||||
const _: () = assert!(BIG_ICON_Y + 16.0 <= BIG_LABEL_Y);
|
||||
|
||||
/// Shortcut text for a command (`⌘B` on macOS, `Ctrl+B` elsewhere): its first key in effect,
|
||||
/// custom keys included (Customize Keyboard).
|
||||
|
|
@ -154,14 +164,14 @@ fn big_button(ui: &mut Ui, app: &mut WordApp, icon: &str, label: &str, id: &str,
|
|||
keytip_badge(ui, r, id, &label.replace('\n', " "));
|
||||
let on = enabled(app, id);
|
||||
bg(ui, r, &resp, checked, &t);
|
||||
let ic = Rect::from_center_size(pos2(r.center().x, r.min.y + 20.0), vec2(32.0, 32.0));
|
||||
let ic = Rect::from_center_size(pos2(r.center().x, r.min.y + BIG_ICON_Y), vec2(32.0, 32.0));
|
||||
let (c, a) = if on { (t.icon, t.accent) } else { (t.text_disabled, t.text_disabled) };
|
||||
icons::paint(ui.painter(), ic, icon, c, a);
|
||||
let mut y = r.min.y + 43.0;
|
||||
let mut y = r.min.y + BIG_LABEL_Y;
|
||||
let lines: Vec<&str> = label.split('\n').collect();
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
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;
|
||||
y += LABEL_PITCH;
|
||||
}
|
||||
let resp = tooltip(app, resp, &label.replace('\n', " "), id);
|
||||
command_menu(&resp, app, id);
|
||||
|
|
@ -263,12 +273,12 @@ pub fn menu_button(
|
|||
let (r, resp) = ui.allocate_exact_size(vec2(w, CONTENT_H), Sense::click());
|
||||
keytip_badge(ui, r, "menu", tip);
|
||||
bg(ui, r, &resp, false, &t);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.center().x, r.min.y + 20.0), vec2(32.0, 32.0)), icon, c, a);
|
||||
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.center().x, r.min.y + BIG_ICON_Y), vec2(32.0, 32.0)), icon, c, a);
|
||||
let lines: Vec<&str> = label.unwrap_or("").split('\n').collect();
|
||||
let mut y = r.min.y + 43.0;
|
||||
let mut y = r.min.y + BIG_LABEL_Y;
|
||||
for (i, l) in lines.iter().enumerate() {
|
||||
label_line(ui, r.center().x, y, l, regular(11.5), text, i + 1 == lines.len());
|
||||
y += 13.0;
|
||||
y += LABEL_PITCH;
|
||||
}
|
||||
resp
|
||||
} else {
|
||||
|
|
@ -313,9 +323,10 @@ pub fn group_titled(ui: &mut Ui, id_title: &str, title: &str, launcher: Option<&
|
|||
});
|
||||
let r = inner.response.rect;
|
||||
let r = Rect::from_min_max(start, pos2(r.max.x, start.y + CONTENT_H + LABEL_H));
|
||||
ui.painter().text(pos2(r.center().x, r.max.y - 8.0), Align2::CENTER_CENTER, title, regular(11.0), t.group_label);
|
||||
let title_y = r.max.y - LABEL_H / 2.0;
|
||||
ui.painter().text(pos2(r.center().x, title_y), Align2::CENTER_CENTER, title, regular(11.0), t.group_label);
|
||||
if let Some(cmd) = launcher {
|
||||
let lr = Rect::from_center_size(pos2(r.max.x - 6.0, r.max.y - 8.0), vec2(11.0, 11.0));
|
||||
let lr = Rect::from_center_size(pos2(r.max.x - 6.0, title_y), vec2(11.0, 11.0));
|
||||
let resp = ui.interact(lr, ui.id().with(("launch", id_title)), Sense::click());
|
||||
if resp.hovered() {
|
||||
ui.painter().rect_filled(lr.expand(2.0), 2.0, t.hover);
|
||||
|
|
@ -822,6 +833,17 @@ fn icon_text_button_with(ui: &mut Ui, icon: &str, text: &str, font: egui::FontId
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// #653: a large button's label line is no taller than `LABEL_LINE_H` in the interface font, so
|
||||
/// two lines (checked at compile time) end above the group title row.
|
||||
#[test]
|
||||
fn big_button_label_lines_fit_their_slot() {
|
||||
let ctx = egui::Context::default();
|
||||
ctx.set_fonts(crate::theme::font_definitions(false, false));
|
||||
ctx.run_ui(egui::RawInput::default(), |_| {}).drop_without_applying_deltas();
|
||||
let h = ctx.fonts_mut(|f| f.layout_no_wrap("Page Number Mail Merge gjpqy".into(), crate::theme::regular(11.5), Color32::WHITE).size().y);
|
||||
assert!(h <= LABEL_LINE_H, "label line is {h} px tall");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drawn_buttons_fade_in_on_hover_and_darken_while_pressed() {
|
||||
// #305's drawn replacements for ✕, 💬, ✎ and ➕ must react like egui's buttons.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# UI parity with Microsoft Word
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (menu buttons grey out, #658; previously 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
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** trivial (ribbon labels clear the group titles, #653; previously trivial: menu buttons grey out, #658; previously 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 | Two-line labels on large ribbon buttons end above the group title row (icon and label moved up, title row 18 px; shared constants with a compile-time check) (#653) |
|
||||
| 2026-10-11 | trivial | Menu buttons (Header, Page Number, Breaks, Wrap Text, table Select/Delete/AutoFit…) grey out and don't open while the command their menu is about is disabled, like the other ribbon buttons (#658) |
|
||||
| 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) |
|
||||
|
|
|
|||
Loading…
Reference in New Issue