mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-27 20:07:19 -06:00
i18n audit: full ribbon sweep, and the table style sample in the interface language (re-lands #313's last three commits) (#411)
* i18n audit: start every ribbon click from a fresh app; translate the table style preview The sweep pressed Escape after each click, and in an equation Escape leaves it, so the Equation tab vanished after its first control and the rest of its menus (Convert, Justification, …) were never opened. Each control now gets a fresh app set up the same way. The narrower allowance for short text (only icon lettering and initials) also catches short words. It found the sample table in the Table Style dialog (Mon, Tue, North, Total, …), now translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtHKQ7wrZm3uBuaG1Ye8po * i18n audit: click the ribbon's bottom row too The sweep only clicked controls centred between y = 52 and 125, which took in the tab row and left out each group's bottom row (Equation › Convert and Justification among them). The band is now taken from the layout: below the tab row and above the document area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtHKQ7wrZm3uBuaG1Ye8po * i18n audit: lay the app out wide enough that no ribbon tab overflows At 1440 pt the Equation tab ran past the window, so its last controls (Justification among them) sat behind the ribbon's scroll arrow and were never clicked. The audit window is now 2560 pt wide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtHKQ7wrZm3uBuaG1Ye8po --------- 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:
co-authored by
Claude Opus 5.5
Dmitrii Khlapov
Brandon Thomas
parent
2c8439b24c
commit
8fde69767a
@@ -491,6 +491,8 @@ fn table_style_preview(ui: &mut Ui, style: Option<&wordcraft_doc::styles::TableS
|
||||
&& !header
|
||||
&& (ri.saturating_sub(usize::from(look.header_row)) / p.band_size.unwrap_or(1).clamp(1, 1000) as usize).is_multiple_of(2);
|
||||
for (ci, txt) in row.iter().enumerate() {
|
||||
// Sample words in the interface language; numbers as they are.
|
||||
let txt = &if txt.chars().any(char::is_alphabetic) { tl!(txt) } else { *txt };
|
||||
let first = look.first_column && ci == 0;
|
||||
let last = look.last_column && ci + 1 == COLS;
|
||||
let col_band = look.banded_columns && !first && ci.saturating_sub(usize::from(look.first_column)).is_multiple_of(2);
|
||||
|
||||
@@ -26,7 +26,8 @@ fn texts(shape: &egui::Shape, out: &mut Vec<String>) {
|
||||
/// Run a frame per entry of `steps` (its events), then a few quiet ones, and return every text
|
||||
/// drawn in the last.
|
||||
fn frames(ctx: &egui::Context, app: &mut WordApp, steps: Vec<Vec<egui::Event>>) -> Vec<String> {
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1440.0, 900.0));
|
||||
// Wide enough that no ribbon tab overflows behind its scroll arrows.
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(2560.0, 900.0));
|
||||
let mut drawn = Vec::new();
|
||||
let n = steps.len() + 3;
|
||||
let mut steps = steps.into_iter();
|
||||
@@ -61,18 +62,22 @@ fn escape() -> Vec<Vec<egui::Event>> {
|
||||
vec![vec![egui::Event::Key { key: egui::Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]]
|
||||
}
|
||||
|
||||
/// Centres of the enabled, clickable widgets drawn last frame in the ribbon band.
|
||||
/// Centres of the enabled, clickable widgets drawn last frame in the ribbon: below the tab row
|
||||
/// and above the document area (the largest clickable widget).
|
||||
fn ribbon_controls(ctx: &egui::Context) -> Vec<egui::Pos2> {
|
||||
ctx.viewport(|v| {
|
||||
v.prev_pass
|
||||
let widgets: Vec<&egui::WidgetRect> = v
|
||||
.prev_pass
|
||||
.widgets
|
||||
.layers()
|
||||
.filter(|(layer, _)| matches!(layer.order, egui::Order::Background | egui::Order::Middle))
|
||||
.flat_map(|(_, w)| w.iter())
|
||||
.filter(|w| w.enabled && w.sense.senses_click() && w.rect.width() < 300.0)
|
||||
.map(|w| w.rect.center())
|
||||
.filter(|c| (52.0..125.0).contains(&c.y))
|
||||
.collect()
|
||||
.filter(|w| w.enabled && w.sense.senses_click())
|
||||
.collect();
|
||||
let page_top = widgets.iter().max_by(|a, b| a.rect.area().total_cmp(&b.rect.area())).map_or(f32::INFINITY, |w| w.rect.min.y);
|
||||
// The tab row ends where the ribbon's first group label row starts; tabs sit near y = 53.
|
||||
let tabs_bottom = 64.0;
|
||||
widgets.iter().filter(|w| w.rect.width() < 300.0).map(|w| w.rect.center()).filter(|c| c.y > tabs_bottom && c.y < page_top).collect()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,7 +128,10 @@ impl Exempt {
|
||||
let status = app.status_msg.iter().map(|(m, _)| m).chain(&app.read_aloud_error).any(|m| text.contains(m.as_str()));
|
||||
// A theme's fonts after its (translated) name: `Studio: Georgia / Georgia`.
|
||||
let theme_fonts = plain.strip_prefix(": ").is_some_and(|f| f.split(" / ").count() == 2);
|
||||
plain.chars().count() <= 3 // lettering inside icons (`Aa`, `abc`) and initials
|
||||
let icon_lettering = matches!(plain, "Aa" | "ab" | "abc" | "ac" | "cd" | "ab-" | "fx" | "ABC");
|
||||
let initials = plain.chars().count() == 2 && plain.chars().all(|c| c.is_ascii_uppercase());
|
||||
icon_lettering // drawn inside icons
|
||||
|| initials // the account button
|
||||
|| status
|
||||
|| theme_fonts
|
||||
|| is_shortcut()
|
||||
@@ -202,8 +210,8 @@ fn untranslated_interface_text() {
|
||||
record(&app, &format!("pane {pane}"), drawn);
|
||||
let _ = app.run(pane, json!({"value": false}));
|
||||
}
|
||||
// Right-click on the page.
|
||||
let drawn = frames(&ctx, &mut app, click(egui::pos2(720.0, 520.0), egui::PointerButton::Secondary));
|
||||
// Right-click on the page (centred in the window).
|
||||
let drawn = frames(&ctx, &mut app, click(egui::pos2(1280.0, 520.0), egui::PointerButton::Secondary));
|
||||
record(&app, "context menu", drawn);
|
||||
frames(&ctx, &mut app, escape());
|
||||
// Contextual tabs: a table, then an equation.
|
||||
@@ -219,28 +227,31 @@ fn untranslated_interface_text() {
|
||||
record(&app, "tab Equation", drawn);
|
||||
// Every ribbon control on every tab, and whatever menu or dialog it opens. A fresh app per
|
||||
// tab keeps one tab's commands from changing the next.
|
||||
// A fresh app per control, set up the same way, so no click changes what the next one hits
|
||||
// (Escape, for one, leaves an equation and takes the Equation tab with it).
|
||||
let contextual = ["Table Design", "Table Layout", "Equation"];
|
||||
for tab in tabs.iter().chain(&contextual) {
|
||||
let open = |tab: &str| {
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = fresh_app(&ctx);
|
||||
match *tab {
|
||||
match tab {
|
||||
"Table Design" | "Table Layout" => drop(app.run("insert.table", json!({"rows": 2, "cols": 2}))),
|
||||
"Equation" => drop(app.run("insert.equation", json!({}))),
|
||||
_ => {}
|
||||
}
|
||||
let _ = app.run("ui.tab", json!({"tab": tab}));
|
||||
frame(&ctx, &mut app);
|
||||
for at in ribbon_controls(&ctx) {
|
||||
(ctx, app)
|
||||
};
|
||||
for tab in tabs.iter().chain(&contextual) {
|
||||
let (ctx, _) = open(tab);
|
||||
let controls = ribbon_controls(&ctx);
|
||||
for at in controls {
|
||||
let (ctx, mut app) = open(tab);
|
||||
// Hover first (tooltips), then click (menus, dialogs).
|
||||
let hovered = frames(&ctx, &mut app, vec![vec![egui::Event::PointerMoved(at)]; 3]);
|
||||
record(&app, &format!("{tab} › hover"), hovered);
|
||||
let drawn = frames(&ctx, &mut app, click(at, egui::PointerButton::Primary));
|
||||
record(&app, &format!("{tab} › click"), drawn);
|
||||
frames(&ctx, &mut app, escape());
|
||||
app.dialog = None;
|
||||
let _ = app.run("ui.backstage", json!({"value": false}));
|
||||
let _ = app.run("ui.tab", json!({"tab": tab}));
|
||||
frame(&ctx, &mut app);
|
||||
}
|
||||
}
|
||||
pseudo::set(false);
|
||||
|
||||
Reference in New Issue
Block a user