diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 6119ccc9..6baf943b 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3586,3 +3586,8 @@ Export the labels to a preset file… Exportar las etiquetas a un archivo de ajuste preestablecido… Show the presets folder Mostrar la carpeta de ajustes preestablecidos Delete this preset Eliminar este ajuste preestablecido + Opens in your web browser: {url} Se abre en el navegador web: {url} + Saves this session's log and shows it in your file manager (no web page). Guarda el registro de esta sesión y lo muestra en el explorador de archivos (no abre ninguna página web). + Shows a report on this computer in a FilmCraft window (no web page). Muestra un informe sobre este equipo en una ventana de FilmCraft (no abre ninguna página web). + Shows the version, community links and credits in a FilmCraft window (no web page). Muestra la versión, los enlaces de la comunidad y los créditos en una ventana de FilmCraft (no abre ninguna página web). + Couldn't open your web browser ({error}). The address is copied to the clipboard: {url} No se pudo abrir el navegador web ({error}). La dirección se ha copiado al portapapeles: {url} diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index d142b44b..e641321b 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -3588,3 +3588,8 @@ Export the labels to a preset file… ラベルをプリセットファイルに書き出し… Show the presets folder プリセットフォルダーを表示 Delete this preset このプリセットを削除 + Opens in your web browser: {url} ウェブブラウザーで開きます:{url} + Saves this session's log and shows it in your file manager (no web page). このセッションのログを保存し、ファイルマネージャーで表示します(ウェブページは開きません)。 + Shows a report on this computer in a FilmCraft window (no web page). このコンピューターのレポートを FilmCraft のウィンドウに表示します(ウェブページは開きません)。 + Shows the version, community links and credits in a FilmCraft window (no web page). バージョン、コミュニティへのリンク、クレジットを FilmCraft のウィンドウに表示します(ウェブページは開きません)。 + Couldn't open your web browser ({error}). The address is copied to the clipboard: {url} ウェブブラウザーを開けませんでした({error})。アドレスをクリップボードにコピーしました:{url} diff --git a/crates/ui-egui/src/i18n/zh-cn.tsv b/crates/ui-egui/src/i18n/zh-cn.tsv index 15bc785f..423f2c07 100644 --- a/crates/ui-egui/src/i18n/zh-cn.tsv +++ b/crates/ui-egui/src/i18n/zh-cn.tsv @@ -3564,3 +3564,8 @@ Export the labels to a preset file… 将标签导出为预设文件… Show the presets folder 显示预设文件夹 Delete this preset 删除此预设 + Opens in your web browser: {url} 在网页浏览器中打开:{url} + Saves this session's log and shows it in your file manager (no web page). 保存本次会话的日志并在文件管理器中显示(不打开网页)。 + Shows a report on this computer in a FilmCraft window (no web page). 在 FilmCraft 窗口中显示本机的报告(不打开网页)。 + Shows the version, community links and credits in a FilmCraft window (no web page). 在 FilmCraft 窗口中显示版本、社区链接和致谢(不打开网页)。 + Couldn't open your web browser ({error}). The address is copied to the clipboard: {url} 无法打开网页浏览器({error})。地址已复制到剪贴板:{url} diff --git a/crates/ui-egui/src/links.rs b/crates/ui-egui/src/links.rs index 6d980e6c..5c4d257c 100644 --- a/crates/ui-egui/src/links.rs +++ b/crates/ui-egui/src/links.rs @@ -31,15 +31,39 @@ pub fn url_for(command: &str) -> Option<&'static str> { ALL.iter().find(|(id, _, _)| *id == command).map(|(_, _, u)| *u) } +/// What a Help menu item does, shown when the pointer rests on it: the address a link opens in +/// the web browser, or that the item stays inside FilmCraft. +pub fn menu_hint(command: &str) -> Option { + let url = if command == "help.filmcraftHelp" { Some(crate::panels::keyboard::HELP_URL) } else { url_for(command) }; + if let Some(url) = url { + return Some(tlf!("Opens in your web browser: {url}", url)); + } + Some( + match command { + "help.revealLogFiles" => tl!("Saves this session's log and shows it in your file manager (no web page)."), + "help.systemCompatibilityReport" => tl!("Shows a report on this computer in a FilmCraft window (no web page)."), + "app.about" => tl!("Shows the version, community links and credits in a FilmCraft window (no web page)."), + _ => return None, + } + .to_string(), + ) +} + /// Open `url` in the system browser (a new tab on the web). The desktop host's /// [`HostHooks::open_url`](crate::HostHooks::open_url) opens it; without the hook, or when it /// fails, it goes through `ctx.open_url`, which only does something on the web: the native egui -/// backend is built without its `links` feature, so there it would only log (#642). +/// backend is built without its `links` feature, so there it would only log (#642). When the +/// host can't open a browser, the address goes to the clipboard and the status bar says so, so +/// the click is never silently lost. pub fn open(app: &mut crate::FilmcraftApp, ctx: &egui::Context, url: &str) { if let Some(f) = app.hooks.open_url.as_mut() { match f(url) { Ok(()) => return, - Err(e) => log::warn!("can't open {url} in the browser: {e}"), + Err(error) => { + log::warn!("can't open {url} in the browser: {error}"); + ctx.copy_text(url.to_string()); + app.ui.status = tlf!("Couldn't open your web browser ({error}). The address is copied to the clipboard: {url}", error, url); + } } } ctx.open_url(egui::OpenUrl::new_tab(url)); @@ -87,5 +111,31 @@ mod tests { let fallback: Vec = ctx.output(|o| o.commands.iter().filter_map(|c| if let egui::OutputCommand::OpenUrl(u) = c { Some(u.url.clone()) } else { None }).collect()); assert_eq!(fallback, [ISSUES]); + // ... and keeps the address at hand: copied, and named in the status bar with the reason + let copied: Vec = + ctx.output(|o| o.commands.iter().filter_map(|c| if let egui::OutputCommand::CopyText(t) = c { Some(t.clone()) } else { None }).collect()); + assert_eq!(copied, [ISSUES]); + assert!(app.ui.status.contains(ISSUES) && app.ui.status.contains("no browser"), "{}", app.ui.status); + } + + #[test] + fn help_menu_items_say_where_they_go() { + // links name their address; the other Help items say they stay inside FilmCraft + for (id, _, url) in ALL { + let hint = menu_hint(id).unwrap(); + assert!(hint.contains(url) && hint.contains("web browser"), "{id}: {hint}"); + } + assert!(menu_hint("help.filmcraftHelp").unwrap().contains(crate::panels::keyboard::HELP_URL)); + let help_items: Vec = crate::menus::menu_items(&crate::FilmcraftApp::new(filmcraft_engine::Session::default())) + .into_iter() + .filter(|it| it.path.first().map(String::as_str) == Some("Help")) + .map(|it| it.id) + .collect(); + assert!(help_items.len() >= ALL.len() + 3, "{help_items:?}"); + for id in &help_items { + let hint = menu_hint(id).unwrap_or_else(|| panic!("{id} has no hover hint")); + assert!(hint.contains("web browser") || hint.contains("no web page"), "{id}: {hint}"); + } + assert_eq!(menu_hint("edit.undo"), None); } } diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 77833a1b..04b2b891 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -931,7 +931,11 @@ fn menu_entry(ui: &mut egui::Ui, it: &MenuItem, auto: &mut crate::automation::Re if let Some(s) = &it.shortcut { b = b.shortcut_text(shortcut_text(s)); } - let r = ui.add_enabled(it.enabled, b); + let mut r = ui.add_enabled(it.enabled, b); + // Help items say whether they open the web browser (and which address) or stay in FilmCraft + if let Some(hint) = crate::links::menu_hint(&it.id) { + r = r.on_hover_text(hint); + } auto.add(&format!("menu.{}", it.id), r.rect, &it.label); if let Some(color) = swatch { paint_swatch(ui, r.rect, color);