mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 10:46:50 -06:00
Merge PR #898: UI: File › Account shows the version, platform and licence, with an About GridCraft button
crates/ui-egui/tests/file_menu.rs: kept both tests (translated headings, account/About).
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
//! and sheet tabs until Esc or the back arrow returns to the sheet.
|
||||
//!
|
||||
//! A navigation column on the left picks a page (Home, New, Open, Info, Save As, Print, Export,
|
||||
//! Options); Save and Close act at once. Every action runs an existing command through
|
||||
//! Options, Account); Save and Close act at once. Every action runs an existing command through
|
||||
//! [`SheetApp::run`] or an existing dialog through [`SheetApp::open_dialog`]; the state here is
|
||||
//! UI-only and never saved. Recent workbooks are listed by path, with when each was last changed:
|
||||
//! that is asked of the file system on a thread of its own, never while drawing, and only the
|
||||
@@ -36,10 +36,11 @@ pub enum Page {
|
||||
Print,
|
||||
Export,
|
||||
Options,
|
||||
Account,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub const ALL: [Page; 8] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::Options];
|
||||
pub const ALL: [Page; 9] = [Page::Home, Page::New, Page::Open, Page::Info, Page::SaveAs, Page::Print, Page::Export, Page::Options, Page::Account];
|
||||
|
||||
/// The id `ui.backstage` and `ui.dialog {"name": "backstage", "page": …}` take.
|
||||
pub fn id(self) -> &'static str {
|
||||
@@ -52,6 +53,7 @@ impl Page {
|
||||
Page::Print => "print",
|
||||
Page::Export => "export",
|
||||
Page::Options => "options",
|
||||
Page::Account => "account",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +73,7 @@ impl Page {
|
||||
Page::Print => "Print",
|
||||
Page::Export => "Export",
|
||||
Page::Options => "Options",
|
||||
Page::Account => "Account",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +87,7 @@ impl Page {
|
||||
Page::Print => Icon::Print,
|
||||
Page::Export => Icon::Share,
|
||||
Page::Options => Icon::Settings,
|
||||
Page::Account => Icon::Info,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,9 +194,12 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
|
||||
out.close = true;
|
||||
}
|
||||
}
|
||||
// Bottom up: Account sits just above Options, as in Excel.
|
||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::Min), |ui| {
|
||||
if nav_item(ui, Page::Options.icon(), lang.tr("Options"), bs.page == Page::Options, nav_text, nav_sel).clicked() {
|
||||
out.page = Some(Page::Options);
|
||||
for page in [Page::Options, Page::Account] {
|
||||
if nav_item(ui, page.icon(), lang.tr(page.label()), bs.page == page, nav_text, nav_sel).clicked() {
|
||||
out.page = Some(page);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -211,6 +218,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
|
||||
Page::Print => print(app, ui, &mut bs, &mut out),
|
||||
Page::Export => export(app, ui, &mut out),
|
||||
Page::Options => options(app, ui),
|
||||
Page::Account => account(app, ui),
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -996,6 +1004,51 @@ fn options(app: &mut SheetApp, ui: &mut Ui) {
|
||||
dim(ui, lang.tr("Calculation settings are saved with the workbook."));
|
||||
}
|
||||
|
||||
/// The platform the app was built for, e.g. "macOS (aarch64)"; the web build reports "Web".
|
||||
fn platform() -> String {
|
||||
let os = match std::env::consts::OS {
|
||||
"macos" => "macOS",
|
||||
"windows" => "Windows",
|
||||
"linux" => "Linux",
|
||||
"freebsd" => "FreeBSD",
|
||||
"openbsd" => "OpenBSD",
|
||||
"netbsd" => "NetBSD",
|
||||
_ if cfg!(target_arch = "wasm32") => return "Web".into(),
|
||||
other => other,
|
||||
};
|
||||
format!("{os} ({})", std::env::consts::ARCH)
|
||||
}
|
||||
|
||||
/// Excel's File › Account: product information and the About window.
|
||||
fn account(app: &mut SheetApp, ui: &mut Ui) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let lang = app.ui.language;
|
||||
section(ui, lang, "Product Information");
|
||||
ui.label(RichText::new("GridCraft").font(theme::ui_bold(16.0)).color(t.text));
|
||||
ui.add_space(8.0);
|
||||
egui::Grid::new("backstage_account").num_columns(2).spacing(vec2(18.0, 8.0)).show(ui, |ui| {
|
||||
for (label, value) in
|
||||
[("Version", env!("CARGO_PKG_VERSION").to_string()), ("Platform", platform()), ("Licence", env!("CARGO_PKG_LICENSE").to_string())]
|
||||
{
|
||||
ui.label(RichText::new(lang.tr(label)).color(t.text_dim));
|
||||
ui.label(value);
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
ui.add_space(18.0);
|
||||
ui.horizontal(|ui| {
|
||||
if square_button(ui, Icon::Info, lang.tr("About GridCraft")).clicked() {
|
||||
app.open_dialog("about", json!({}));
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(14.0);
|
||||
ui.label(RichText::new(lang.tr("About GridCraft")).font(theme::ui_bold(13.5)).color(t.text));
|
||||
dim(ui, lang.tr("Version, contributors and the AI models that helped"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1076,7 +1129,7 @@ mod tests {
|
||||
assert_eq!(Page::parse(page.id()), Some(page));
|
||||
}
|
||||
assert_eq!(Page::parse(" SAVEAS "), Some(Page::SaveAs));
|
||||
assert_eq!(Page::parse("account"), None);
|
||||
assert_eq!(Page::parse("feedback"), None);
|
||||
assert_eq!(Page::parse(""), None);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
|
||||
("options.autoComplete", "Enable AutoComplete for cell values", "{on?: bool} (toggles without it) → {on}"),
|
||||
("view.interfaceScale", "Interface Size", "{percent: 75–200 | \"auto\"} (100 = auto; clamped)"),
|
||||
("ui.dialog", "Open Dialog", ""),
|
||||
("ui.backstage", "File (Backstage view)", "{page?: home|new|open|info|saveAs|print|export|options|close}"),
|
||||
("ui.backstage", "File (Backstage view)", "{page?: home|new|open|info|saveAs|print|export|options|account|close}"),
|
||||
(
|
||||
"ui.printSettings",
|
||||
"Print Settings",
|
||||
|
||||
@@ -519,6 +519,13 @@ const JAPANESE: &[(&str, &str)] = &[
|
||||
("Export", "エクスポート"),
|
||||
("Close", "閉じる"),
|
||||
("Options", "オプション"),
|
||||
("Account", "アカウント"),
|
||||
("Product Information", "製品情報"),
|
||||
("About GridCraft", "GridCraft について"),
|
||||
("Version", "バージョン"),
|
||||
("Platform", "プラットフォーム"),
|
||||
("Licence", "ライセンス"),
|
||||
("Version, contributors and the AI models that helped", "バージョン、貢献者、支援した AI モデル"),
|
||||
("Blank workbook", "空白のブック"),
|
||||
("Recent", "最近使ったアイテム"),
|
||||
("No recent workbooks yet.", "最近使ったブックはまだありません。"),
|
||||
|
||||
@@ -258,6 +258,12 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Exportovat",
|
||||
"Close" => "Zavřít",
|
||||
"Options" => "Možnosti",
|
||||
"Account" => "Účet",
|
||||
"Product Information" => "Informace o produktu",
|
||||
"About GridCraft" => "O aplikaci GridCraft",
|
||||
"Version" => "Verze",
|
||||
"Platform" => "Platforma",
|
||||
"Licence" => "Licence",
|
||||
"Blank workbook" => "Prázdný sešit",
|
||||
"Recent" => "Naposledy použité",
|
||||
"No recent workbooks yet." => "Zatím žádné naposledy použité sešity.",
|
||||
|
||||
@@ -256,6 +256,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Exportieren",
|
||||
"Close" => "Schließen",
|
||||
"Options" => "Optionen",
|
||||
"Account" => "Konto",
|
||||
"Product Information" => "Produktinformationen",
|
||||
"About GridCraft" => "Info zu GridCraft",
|
||||
"Version" => "Version",
|
||||
"Platform" => "Plattform",
|
||||
"Licence" => "Lizenz",
|
||||
"Version, contributors and the AI models that helped" => "Version, Mitwirkende und die KI-Modelle, die geholfen haben",
|
||||
"Blank workbook" => "Leere Arbeitsmappe",
|
||||
"Recent" => "Zuletzt verwendet",
|
||||
"No recent workbooks yet." => "Noch keine zuletzt verwendeten Arbeitsmappen.",
|
||||
|
||||
@@ -257,6 +257,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Exportar",
|
||||
"Close" => "Cerrar",
|
||||
"Options" => "Opciones",
|
||||
"Account" => "Cuenta",
|
||||
"Product Information" => "Información del producto",
|
||||
"About GridCraft" => "Acerca de GridCraft",
|
||||
"Version" => "Versión",
|
||||
"Platform" => "Plataforma",
|
||||
"Licence" => "Licencia",
|
||||
"Version, contributors and the AI models that helped" => "Versión, colaboradores y los modelos de IA que ayudaron",
|
||||
"Blank workbook" => "Libro en blanco",
|
||||
"Recent" => "Recientes",
|
||||
"No recent workbooks yet." => "Todavía no hay libros recientes.",
|
||||
|
||||
@@ -342,6 +342,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Exporter",
|
||||
"Close" => "Fermer",
|
||||
"Options" => "Options",
|
||||
"Account" => "Compte",
|
||||
"Product Information" => "Informations sur le produit",
|
||||
"About GridCraft" => "À propos de GridCraft",
|
||||
"Version" => "Version",
|
||||
"Platform" => "Plateforme",
|
||||
"Licence" => "Licence",
|
||||
"Version, contributors and the AI models that helped" => "Version, contributeurs et les modèles d'IA qui ont aidé",
|
||||
"Blank workbook" => "Classeur vide",
|
||||
"Recent" => "Récent",
|
||||
"No recent workbooks yet." => "Aucun classeur récent pour le moment.",
|
||||
|
||||
@@ -344,6 +344,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "내보내기",
|
||||
"Close" => "닫기",
|
||||
"Options" => "옵션",
|
||||
"Account" => "계정",
|
||||
"Product Information" => "제품 정보",
|
||||
"About GridCraft" => "GridCraft 정보",
|
||||
"Version" => "버전",
|
||||
"Platform" => "플랫폼",
|
||||
"Licence" => "라이선스",
|
||||
"Version, contributors and the AI models that helped" => "버전, 기여자 및 도움을 준 AI 모델",
|
||||
"Blank workbook" => "새 통합 문서",
|
||||
"Recent" => "최근 항목",
|
||||
"No recent workbooks yet." => "최근 통합 문서가 아직 없습니다.",
|
||||
|
||||
@@ -345,6 +345,11 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Exportar",
|
||||
"Close" => "Fechar",
|
||||
"Options" => "Opções",
|
||||
"Account" => "Conta",
|
||||
"Product Information" => "Informações do produto",
|
||||
"Version" => "Versão",
|
||||
"Platform" => "Plataforma",
|
||||
"Licence" => "Licença",
|
||||
"Blank workbook" => "Pasta de trabalho em branco",
|
||||
"Recent" => "Recentes",
|
||||
"No recent workbooks yet." => "Ainda não há pastas de trabalho recentes.",
|
||||
|
||||
@@ -344,6 +344,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Экспорт",
|
||||
"Close" => "Закрыть",
|
||||
"Options" => "Параметры",
|
||||
"Account" => "Учетная запись",
|
||||
"Product Information" => "Сведения о продукте",
|
||||
"About GridCraft" => "О программе GridCraft",
|
||||
"Version" => "Версия",
|
||||
"Platform" => "Платформа",
|
||||
"Licence" => "Лицензия",
|
||||
"Version, contributors and the AI models that helped" => "Версия, участники и модели ИИ, которые помогали",
|
||||
"Blank workbook" => "Пустая книга",
|
||||
"Recent" => "Последние",
|
||||
"No recent workbooks yet." => "Недавних книг пока нет.",
|
||||
|
||||
@@ -336,6 +336,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "Izvezi",
|
||||
"Close" => "Zatvori",
|
||||
"Options" => "Opcije",
|
||||
"Account" => "Nalog",
|
||||
"Product Information" => "Informacije o proizvodu",
|
||||
"About GridCraft" => "O programu GridCraft",
|
||||
"Version" => "Verzija",
|
||||
"Platform" => "Platforma",
|
||||
"Licence" => "Licenca",
|
||||
"Version, contributors and the AI models that helped" => "Verzija, saradnici i AI modeli koji su pomogli",
|
||||
"Blank workbook" => "Prazna radna sveska",
|
||||
"Recent" => "Nedavno",
|
||||
"No recent workbooks yet." => "Još nema nedavnih radnih svezaka.",
|
||||
|
||||
@@ -342,6 +342,13 @@ pub(super) fn translate(text: &str) -> Option<&'static str> {
|
||||
"Export" => "导出",
|
||||
"Close" => "关闭",
|
||||
"Options" => "选项",
|
||||
"Account" => "账户",
|
||||
"Product Information" => "产品信息",
|
||||
"About GridCraft" => "关于 GridCraft",
|
||||
"Version" => "版本",
|
||||
"Platform" => "平台",
|
||||
"Licence" => "许可证",
|
||||
"Version, contributors and the AI models that helped" => "版本、贡献者以及参与协助的 AI 模型",
|
||||
"Blank workbook" => "空白工作簿",
|
||||
"Recent" => "最近",
|
||||
"No recent workbooks yet." => "还没有最近使用的工作簿。",
|
||||
|
||||
@@ -122,7 +122,7 @@ fn file_options_opens_the_options_page_and_pages_are_reachable_by_command() {
|
||||
h.run_steps(2);
|
||||
assert_eq!(page(&h), Some(id));
|
||||
}
|
||||
assert!(h.state_mut().run("ui.backstage", serde_json::json!({"page": "account"})).is_err());
|
||||
assert!(h.state_mut().run("ui.backstage", serde_json::json!({"page": "feedback"})).is_err());
|
||||
h.state_mut().run("ui.backstage", serde_json::json!({"page": "close"})).unwrap();
|
||||
assert!(h.state().backstage.is_none());
|
||||
// The back arrow returns to the sheet too.
|
||||
@@ -159,6 +159,20 @@ fn translated_options_headings_show_no_context_prefix() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_account_shows_the_version_and_opens_about() {
|
||||
let mut h = harness(Services::default());
|
||||
choose(&mut h, "Account");
|
||||
assert_eq!(page(&h), Some("account"));
|
||||
assert!(h.state().dialog.is_none());
|
||||
// The About dialog also shows the version; it must not be open yet.
|
||||
h.get_by_label(env!("CARGO_PKG_VERSION"));
|
||||
h.get_by_label(env!("CARGO_PKG_LICENSE"));
|
||||
click(&mut h, "About GridCraft");
|
||||
assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("about"));
|
||||
assert_eq!(page(&h), Some("account"), "About opens over the File view, like Protect Workbook on Info");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_as_pdf_is_delivered_as_a_download() {
|
||||
let files = Arc::new(Mutex::new(Vec::<(String, Vec<u8>)>::new()));
|
||||
|
||||
@@ -36,7 +36,7 @@ connection; nothing sent after it runs. Blank lines are skipped.
|
||||
| `ui.edit.begin` / `ui.edit.text` / `ui.edit.commit` / `ui.edit.cancel` | `{text?}` / `{move?: down\|right\|up\|left\|none}` | Drive the cell editor |
|
||||
| `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.backstage` (via `engine.execute`) | `{page?: home\|new\|open\|info\|saveAs\|print\|export\|options\|close}` | Open the File tab's Backstage view on a page, or close it (`ui.inspect` reports the open page as `backstage`) |
|
||||
| `ui.backstage` (via `engine.execute`) | `{page?: home\|new\|open\|info\|saveAs\|print\|export\|options\|account\|close}` | Open the File tab's Backstage view on a page, or close it (`ui.inspect` reports the open page as `backstage`) |
|
||||
| `ui.screenshot` | `{path?}` | PNG of the window (base64 when no path). Animations are settled first: the picture shows the final state |
|
||||
| `ui.set` | `{dark?, formulaBar?, ribbonCollapsed?, animations?}` | UI preferences (`formulaBar` runs `view.formulaBar`) |
|
||||
| `ui.resize` / `ui.focus` | `{width, height}` | Window control |
|
||||
|
||||
Reference in New Issue
Block a user