Merge pull request #295 from vangel76/feat/view-formula-bar-zoom100
View: Formula Bar and Zoom 100% as engine commands (#289) # Conflicts: # ROADMAP.md # crates/ui-egui/src/control.rs # crates/ui-egui/src/lib.rs # docs/target-app-parity.md
This commit is contained in:
commit
4f7144d753
|
|
@ -141,6 +141,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
|
|||
| 2026-10-11 | Selected charts, pictures and shapes can be deleted from the keyboard (#213) |
|
||||
| 2026-10-11 | Ribbon translated into German (266 labels; `de`, `de-DE`, `de-AT`, `de-CH` locale tags resolve to German) |
|
||||
| 2026-10-11 | View › Interface size: a saved 75–200 % scale for the whole window (#230) |
|
||||
| 2026-10-11 | View › Formula Bar and Zoom 100% reachable from the CLI and MCP (#289) |
|
||||
| 2026-10-10 | Ribbon translated into Japanese, Chinese, Korean, Brazilian Portuguese and Russian with system-language default (#31, #104, #82); CJK/Thai/Hebrew font fallback (#38); clear messages for encrypted and `.xls` files (#17); smooth line charts (#62). Progress docs restructured to the craftrules standard; full re-measure (this page). Initial ribbon key tips (#43), in-place text box editing (#84), copy as HTML table (#36). ODS and XLSB data import (#100, #101); recalc linear in the number of range formulas (#66); RISC-V Linux build; Flatpak repository on Pages; web cell-commit trap fixed |
|
||||
| 2026-10-09 | Community fixes: spill range operator `A1#`, sheet-scoped names, protection refuses locked-cell edits, Insert/Delete Cells adjust references, `DATE(1900,2,29)`, SUMIF sizing, `ROUND` at 15 digits, `_xlpm.` LAMBDA parameters in XLSX, vertical scrollbar, system appearance, border previews; v0.4.0 |
|
||||
| 2026-10-08 | v0.2.0 and v0.3.0 releases; Flatpak, AppImage zsync, branded DMG; desktop log file; DirectX 12 default on Windows |
|
||||
|
|
@ -157,6 +158,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
|
|||
| 2026-10-11 | minor | Worksheet functions 502 / 523 (BAHTTEXT); progress log row |
|
||||
| 2026-10-11 | minor | Worksheet functions 502 / 523 (TRIMRANGE); progress log row |
|
||||
| 2026-10-11 | minor | Selected objects can be deleted from the keyboard (#213) |
|
||||
| 2026-10-11 | minor | Catalog 258 → 260 / 290: View › Formula Bar and Zoom 100% (#289) |
|
||||
| 2026-10-10 | minor | Readiness-by-audience table with hours to ~95% per audience; full number unchanged (additive weighted sum) |
|
||||
| 2026-10-10 | minor | Added Mainstream practitioner (~50%) and Essentials user (~63%); languages updated for the 10-10 merges (ribbon in ja, zh, ko, pt-BR, ru); localization 5%→10%; full number re-checked (51.6, unchanged) |
|
||||
| 2026-10-10 | minor | Checked against the alpha gate (six core workflows pass): stays alpha |
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
cmd!("view.showZeros", "Show Zeros", [], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "zeros")),
|
||||
cmd!("view.rightToLeft", "Sheet Right-to-Left", ["Page Layout", "Sheet Options"], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "rtl")),
|
||||
cmd!("view.zoom", "Zoom", ["View", "Zoom"], None, "{percent: 10..400}", has_doc, zoom),
|
||||
cmd!("view.zoom100", "100%", ["View", "Zoom"], None, "{}", has_doc, |s, _| zoom(s, &json!({"percent": 100}))),
|
||||
cmd!(query "view.formulaBar", "Formula Bar", ["View", "Show"], None, "{on?: bool}", always, |s, p| {
|
||||
s.prefs.formula_bar = bool_param(p, "on").unwrap_or(!s.prefs.formula_bar);
|
||||
Ok(json!({"on": s.prefs.formula_bar}))
|
||||
}),
|
||||
cmd!("view.zoomToSelection", "Zoom to Selection", ["View", "Zoom"], None, "{viewWidth?, viewHeight?}", has_doc, zoom_selection),
|
||||
cmd!("pageLayout.orientation", "Orientation", ["Page Layout", "Page Setup"], None, "{orientation: portrait|landscape}", has_doc, orientation),
|
||||
cmd!("pageLayout.size", "Size", ["Page Layout", "Page Setup"], None, "{paper: Letter|Legal|A4|A3|A5|Tabloid|Executive}", has_doc, paper),
|
||||
|
|
@ -336,3 +341,28 @@ fn theme(s: &mut Session, p: &Json) -> Result<Json> {
|
|||
|
||||
#[allow(dead_code)]
|
||||
fn unused(_: CellRef) {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn zoom_100_resets_the_active_sheet_zoom() {
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("view.zoom", json!({"percent": 150})).unwrap();
|
||||
assert_eq!(s.execute("view.zoom100", json!({})).unwrap()["zoom"], 100);
|
||||
assert_eq!(s.doc().unwrap().wb.active().unwrap().zoom, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formula_bar_toggles_without_a_workbook_and_defaults_to_shown() {
|
||||
let mut s = Session::new();
|
||||
assert!(s.prefs.formula_bar);
|
||||
assert_eq!(s.execute("view.formulaBar", json!({})).unwrap()["on"], false);
|
||||
assert_eq!(s.execute("view.formulaBar", json!({"on": true})).unwrap()["on"], true);
|
||||
// A prefs.json saved before the option existed keeps the formula bar shown.
|
||||
let old: crate::Prefs = serde_json::from_str(r#"{"userName": "Ann"}"#).unwrap();
|
||||
assert!(old.formula_bar);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,6 +151,8 @@ pub struct Prefs {
|
|||
pub r1c1: bool,
|
||||
/// Words added with Add to Dictionary.
|
||||
pub user_dictionary: Vec<String>,
|
||||
/// View › Show › Formula Bar (an application option in Excel, not saved in the file).
|
||||
pub formula_bar: bool,
|
||||
}
|
||||
|
||||
impl Default for Prefs {
|
||||
|
|
@ -164,6 +166,7 @@ impl Default for Prefs {
|
|||
sheets_in_new_workbook: 1,
|
||||
r1c1: false,
|
||||
user_dictionary: vec![],
|
||||
formula_bar: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,11 +168,9 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
|
|||
let mut v: Vec<Json> = app.session.commands().into_iter().map(|c| serde_json::to_value(c).unwrap_or_default()).collect();
|
||||
for (id, label, params) in [
|
||||
("ui.ribbonTab", "Ribbon Tab", ""),
|
||||
("view.formulaBar", "Formula Bar", ""),
|
||||
("view.collapseRibbon", "Collapse Ribbon", ""),
|
||||
("view.darkMode", "Dark Mode", ""),
|
||||
("view.theme", "Display Theme", ""),
|
||||
("view.zoom100", "100%", ""),
|
||||
("view.interfaceScale", "Interface Size", "{percent: 75–200 | \"auto\"} (100 = auto; clamped)"),
|
||||
("ui.dialog", "Open Dialog", ""),
|
||||
("app.language.set", "Interface Language", "{language: \"en\"|\"zh\"|\"ja\"|\"ko\"|\"ru\"|\"pt\"|\"de\"} (alias: code)"),
|
||||
|
|
@ -197,12 +195,16 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
|
|||
app.ui.system_theme = false;
|
||||
}
|
||||
if let Some(v) = p.get("formulaBar").and_then(Json::as_bool) {
|
||||
app.ui.formula_bar = v;
|
||||
let _ = app.run("view.formulaBar", json!({"on": v}));
|
||||
}
|
||||
if let Some(v) = p.get("ribbonCollapsed").and_then(Json::as_bool) {
|
||||
app.ui.ribbon_collapsed = v;
|
||||
}
|
||||
ok(serde_json::to_value(&app.ui).unwrap_or_default())
|
||||
let mut out = serde_json::to_value(&app.ui).unwrap_or_default();
|
||||
if let Some(o) = out.as_object_mut() {
|
||||
o.insert("formulaBar".into(), json!(app.session.prefs.formula_bar));
|
||||
}
|
||||
ok(out)
|
||||
}
|
||||
"ui.dialog" => {
|
||||
app.open_dialog(s("name").unwrap_or(""), p.clone());
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ pub struct UiState {
|
|||
pub system_theme: bool,
|
||||
pub ribbon_tab: String,
|
||||
pub ribbon_collapsed: bool,
|
||||
pub formula_bar: bool,
|
||||
pub formula_bar_expanded: bool,
|
||||
pub status_bar: bool,
|
||||
pub recent: Vec<String>,
|
||||
|
|
@ -83,7 +82,6 @@ impl Default for UiState {
|
|||
system_theme: false,
|
||||
ribbon_tab: "Home".into(),
|
||||
ribbon_collapsed: false,
|
||||
formula_bar: true,
|
||||
formula_bar_expanded: false,
|
||||
status_bar: true,
|
||||
recent: vec![],
|
||||
|
|
@ -334,10 +332,6 @@ impl SheetApp {
|
|||
self.ui.ribbon_tab = p.get("tab").and_then(Json::as_str).unwrap_or("Home").to_string();
|
||||
Ok(Json::Null)
|
||||
}
|
||||
"view.formulaBar" => {
|
||||
self.ui.formula_bar = p.get("on").and_then(Json::as_bool).unwrap_or(!self.ui.formula_bar);
|
||||
Ok(json!({"on": self.ui.formula_bar}))
|
||||
}
|
||||
"view.collapseRibbon" => {
|
||||
self.ui.ribbon_collapsed = !self.ui.ribbon_collapsed;
|
||||
Ok(Json::Null)
|
||||
|
|
@ -374,7 +368,6 @@ impl SheetApp {
|
|||
None => Err(format!("missing `percent` ({INTERFACE_SCALE_MIN}–{INTERFACE_SCALE_MAX}, or \"auto\")")),
|
||||
}
|
||||
}
|
||||
"view.zoom100" => return Some(self.session.run("view.zoom", json!({"percent": 100})).inspect(|_| self.after_engine())),
|
||||
"app.language.set" => {
|
||||
// `{"language": "ja"}`; `code` is accepted as an alias.
|
||||
let code = p.get("language").or_else(|| p.get("code")).and_then(Json::as_str).unwrap_or("");
|
||||
|
|
@ -673,7 +666,7 @@ impl SheetApp {
|
|||
self.ribbon_keys(&ctx);
|
||||
ribbon::title_bar(self, ui);
|
||||
ribbon::show(self, ui);
|
||||
if self.ui.formula_bar {
|
||||
if self.session.prefs.formula_bar {
|
||||
formula_bar::show(self, ui);
|
||||
}
|
||||
if self.ui.status_bar {
|
||||
|
|
|
|||
|
|
@ -1642,9 +1642,9 @@ fn view(app: &mut SheetApp, ui: &mut Ui) {
|
|||
let s = app.session.active().and_then(|d| d.wb.active().map(|s| (s.show_gridlines, s.show_headings))).unwrap_or((true, true));
|
||||
let lang = app.ui.language;
|
||||
ui.vertical(|ui| {
|
||||
let mut fb = app.ui.formula_bar;
|
||||
let mut fb = app.session.prefs.formula_bar;
|
||||
if ui.checkbox(&mut fb, lang.tr("Formula Bar")).changed() {
|
||||
app.ui.formula_bar = fb;
|
||||
act(app, "view.formulaBar", json!({"on": fb}));
|
||||
}
|
||||
let mut g = s.0;
|
||||
if ui.checkbox(&mut g, lang.tr("Gridlines")).changed() {
|
||||
|
|
@ -1660,7 +1660,7 @@ fn view(app: &mut SheetApp, ui: &mut Ui) {
|
|||
app.open_dialog("zoom", json!({}));
|
||||
}
|
||||
if big_button(ui, Icon::Search, "100%", "Zoom to 100%", false).clicked() {
|
||||
act(app, "view.zoom", json!({"percent": 100}));
|
||||
act(app, "view.zoom100", json!({}));
|
||||
}
|
||||
if big_button(ui, Icon::Zoom, "Zoom to\nSelection", "Zoom to Selection", false).clicked() {
|
||||
let r = app.grid.cells_rect.unwrap_or(Rect::from_min_size(pos2(0.0, 0.0), vec2(1200.0, 700.0)));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Errors come back as `{"id": …, "ok": false, "error": "…"}`.
|
|||
| `ui.ribbon` | `{tab}` | Switch ribbon tab |
|
||||
| `ui.dialog` / `ui.dialog.set` / `ui.dialog.confirm` / `ui.dialog.cancel` | `{name}` / `{field, value}` | Open, fill in and confirm dialogs |
|
||||
| `ui.screenshot` | `{path?}` | PNG of the window (base64 when no path) |
|
||||
| `ui.set` | `{dark?, formulaBar?, ribbonCollapsed?}` | UI preferences |
|
||||
| `ui.set` | `{dark?, formulaBar?, ribbonCollapsed?}` | UI preferences (`formulaBar` runs `view.formulaBar`) |
|
||||
| `ui.resize` / `ui.focus` | `{width, height}` | Window control |
|
||||
| `app.open` / `app.save` / `app.quit` | `{path}` / `{path?}` | Files and lifetime |
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Each entry is an Excel ribbon or menu command (feature names only). It counts as implemented when GridCraft registers a command with the same id. This measures breadth only; the overall assessment (depth, fidelity, effort) is in [target-app-parity.md](target-app-parity.md).
|
||||
|
||||
**Overall: 258 / 290 commands implemented (89.0%).**
|
||||
**Overall: 260 / 290 commands implemented (89.7%).**
|
||||
|
||||
| Tab | Implemented | Total | Parity |
|
||||
|---|---|---|---|
|
||||
|
|
@ -16,7 +16,7 @@ Each entry is an Excel ribbon or menu command (feature names only). It counts as
|
|||
| Formulas | 23 | 23 | 100% |
|
||||
| Data | 20 | 28 | 71% |
|
||||
| Review | 9 | 18 | 50% |
|
||||
| View | 18 | 20 | 90% |
|
||||
| View | 20 | 20 | 100% |
|
||||
| Automate | 3 | 3 | 100% |
|
||||
| Table Design | 12 | 13 | 92% |
|
||||
| Chart Design | 8 | 8 | 100% |
|
||||
|
|
@ -302,7 +302,7 @@ Missing:
|
|||
- `review.allowEditRanges` Allow Edit Ranges (Protect)
|
||||
- `review.showChanges` Show Changes (Changes)
|
||||
|
||||
## View (18 / 20, 90%)
|
||||
## View (20 / 20, 100%)
|
||||
|
||||
Implemented:
|
||||
|
||||
|
|
@ -311,7 +311,9 @@ Implemented:
|
|||
- `view.pageLayout` Page Layout (Workbook Views)
|
||||
- `view.customViews` Custom Views (Workbook Views)
|
||||
- `view.ruler` Ruler (Show)
|
||||
- `view.formulaBar` Formula Bar (Show)
|
||||
- `view.zoom` Zoom (Zoom)
|
||||
- `view.zoom100` 100% (Zoom)
|
||||
- `view.zoomToSelection` Zoom to Selection (Zoom)
|
||||
- `view.newWindow` New Window (Window)
|
||||
- `view.arrangeAll` Arrange All (Window)
|
||||
|
|
@ -325,11 +327,6 @@ Implemented:
|
|||
- `view.switchWindows` Switch Windows (Window)
|
||||
- `view.macros` Macros (Macros)
|
||||
|
||||
Missing:
|
||||
|
||||
- `view.formulaBar` Formula Bar (Show)
|
||||
- `view.zoom100` 100% (Zoom)
|
||||
|
||||
## Automate (3 / 3, 100%)
|
||||
|
||||
Implemented:
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ The inventory carried over from the 2026-10-07 ROADMAP.md, updated for what land
|
|||
| 2026-10-11 | minor | Worksheet functions 503 / 523 (96.2%; 494 working): PERCENTOF, EUROCONVERT |
|
||||
| 2026-10-11 | minor | Worksheet functions 502 / 523 (96.0%; 493 working): BAHTTEXT |
|
||||
| 2026-10-11 | minor | Worksheet functions 502 / 523 (96.0%; 493 working): TRIMRANGE |
|
||||
| 2026-10-11 | minor | View › Formula Bar and Zoom 100% are engine commands (CLI/MCP); catalog 258 → 260 / 290 (#289) |
|
||||
| 2026-10-10 | minor | Readiness-by-audience table: hours to ~95% for each of the three numbers (full 650–1,050, mainstream 200–310, essentials 60–100). Full number confirmed as the additive weighted sum over the written dimension weights (no change) |
|
||||
| 2026-10-10 | minor | Added Mainstream practitioner (~50%) and Essentials user (~63%) with written weights and discounts, and user-evidence counts; re-checked the full number after the 10-10 merges (charts 38→30, localization 5→10; still 51.6, ~50%) |
|
||||
| 2026-10-10 | minor | Stage checked against the new alpha gate (six core workflows, all pass): stays alpha |
|
||||
|
|
|
|||
Loading…
Reference in New Issue