diff --git a/apps/desktop/public/logo-black.png b/apps/desktop/public/logo-black.png new file mode 100644 index 000000000..c016b386b Binary files /dev/null and b/apps/desktop/public/logo-black.png differ diff --git a/apps/desktop/src/components/auth/LoginPage.vue b/apps/desktop/src/components/auth/LoginPage.vue index 62b94f07d..ab6e916ca 100644 --- a/apps/desktop/src/components/auth/LoginPage.vue +++ b/apps/desktop/src/components/auth/LoginPage.vue @@ -4,6 +4,7 @@ import { useI18n } from "vue-i18n"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Lock, Loader2, ShieldCheck } from "lucide-vue-next"; +import AppLogo from "@/components/icons/AppLogo.vue"; const props = withDefaults( defineProps<{ @@ -55,7 +56,7 @@ async function submit() { >
- DBX +

DBX

diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 01b25a08b..0f1a7f633 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -36,6 +36,7 @@ import { type AiProvider, type AiApiStyle, type EditorTheme, + type DesktopIconTheme, } from "@/stores/settingsStore"; import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes"; import { isTauriRuntime } from "@/lib/tauriRuntime"; @@ -66,6 +67,7 @@ import type { SqlSnippet } from "@/types/database"; import { uuid } from "@/lib/utils"; import { DEFAULT_SQL_SNIPPETS } from "@/lib/sqlCompletion"; import AiProviderLogo from "@/components/icons/AiProviderLogo.vue"; +import AppLogo from "@/components/icons/AppLogo.vue"; import type { AppThemeAppearance } from "@/lib/appTheme"; import { useConnectionStore } from "@/stores/connectionStore"; @@ -93,6 +95,7 @@ const editExecuteMode = ref(settingsStore.editorSettings.executeMode); const editWordWrap = ref(settingsStore.editorSettings.wordWrap); const editAppLayout = ref(settingsStore.editorSettings.appLayout); const editShowTrayIcon = ref(settingsStore.desktopSettings.show_tray_icon); +const editIconTheme = ref(settingsStore.desktopSettings.icon_theme); const editShowColumnCommentsInHeader = ref(settingsStore.editorSettings.showColumnCommentsInHeader); const editCompactColumnHeaderActions = ref(settingsStore.editorSettings.compactColumnHeaderActions); const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize); @@ -228,6 +231,7 @@ watch( editWordWrap.value = settingsStore.editorSettings.wordWrap; editAppLayout.value = settingsStore.editorSettings.appLayout; editShowTrayIcon.value = settingsStore.desktopSettings.show_tray_icon; + editIconTheme.value = settingsStore.desktopSettings.icon_theme; editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader; editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize; @@ -266,6 +270,7 @@ function hasChanges(): boolean { editWordWrap.value !== settingsStore.editorSettings.wordWrap || editAppLayout.value !== settingsStore.editorSettings.appLayout || editShowTrayIcon.value !== settingsStore.desktopSettings.show_tray_icon || + editIconTheme.value !== settingsStore.desktopSettings.icon_theme || editShowColumnCommentsInHeader.value !== settingsStore.editorSettings.showColumnCommentsInHeader || editCompactColumnHeaderActions.value !== settingsStore.editorSettings.compactColumnHeaderActions || editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize || @@ -305,6 +310,7 @@ async function persistSettings() { }); await settingsStore.updateDesktopSettings({ show_tray_icon: editShowTrayIcon.value, + icon_theme: editIconTheme.value, }); if (sidebarObjectDisplayChanged) { await connectionStore.refreshAllTree(); @@ -329,6 +335,7 @@ function resetDefaults() { editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap; editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout; editShowTrayIcon.value = DEFAULT_DESKTOP_SETTINGS.show_tray_icon; + editIconTheme.value = DEFAULT_DESKTOP_SETTINGS.icon_theme; editShowColumnCommentsInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnCommentsInHeader; editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions; editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize; @@ -362,6 +369,10 @@ function setSidebarObjectDisplay(value: "grouped" | "simple") { editSidebarObjectDisplay.value = value; } +function setIconTheme(value: DesktopIconTheme) { + editIconTheme.value = value; +} + function onShortcutChange(actionId: ShortcutActionId, value: any) { if (typeof value !== "string") return; const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === actionId); @@ -591,6 +602,7 @@ watch( await settingsStore.initAiConfig(); await settingsStore.initDesktopSettings(); editShowTrayIcon.value = settingsStore.desktopSettings.show_tray_icon; + editIconTheme.value = settingsStore.desktopSettings.icon_theme; webdavPassword.value = ""; await refreshWebDavPasswordStatus(); syncAiEditState(); @@ -1164,6 +1176,43 @@ watch(

+ +
+ +
+ + +
+
+
- DBX + {{ t("settings.officialDocs") }}
diff --git a/apps/desktop/src/components/icons/AppLogo.vue b/apps/desktop/src/components/icons/AppLogo.vue new file mode 100644 index 000000000..d25684c0a --- /dev/null +++ b/apps/desktop/src/components/icons/AppLogo.vue @@ -0,0 +1,12 @@ + + + diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts index 629d77686..8f4f1c864 100644 --- a/apps/desktop/src/i18n/locales/en.ts +++ b/apps/desktop/src/i18n/locales/en.ts @@ -1456,6 +1456,11 @@ export default { appLayoutSeparatedDescription: "Clear panel separation with block-style tabs.", appLayoutClassic: "Classic compact", appLayoutClassicDescription: "Continuous tab bar with compact panel headers.", + iconTheme: "Icon and logo style", + iconThemeDefault: "Default", + iconThemeDefaultDescription: "Use the color DBX app icon.", + iconThemeBlack: "Black", + iconThemeBlackDescription: "Use the black DBX mark for the window, tray, and logo.", showTrayIcon: "Show tray/menu bar icon", showTrayIconDescription: "When disabled, no icon is shown, but closing the window still hides DBX in the background as before.", diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts index e525cf1ea..9aba0e583 100644 --- a/apps/desktop/src/i18n/locales/es.ts +++ b/apps/desktop/src/i18n/locales/es.ts @@ -1349,6 +1349,11 @@ export default { appLayoutSeparatedDescription: "Separación clara de paneles con pestañas en bloque.", appLayoutClassic: "Clásico compacto", appLayoutClassicDescription: "Barra de pestañas continua con encabezados de panel compactos.", + iconTheme: "Estilo de icono y logo", + iconThemeDefault: "Predeterminado", + iconThemeDefaultDescription: "Usar el icono de color de DBX.", + iconThemeBlack: "Negro", + iconThemeBlackDescription: "Usar la marca negra de DBX en ventana, bandeja y logo.", showTrayIcon: "Mostrar icono en bandeja/barra de menú", showTrayIconDescription: "Si está desactivado, no se muestra el icono, pero cerrar la ventana sigue ocultando DBX en segundo plano como antes.", diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts index 0b887adf0..20825570c 100644 --- a/apps/desktop/src/i18n/locales/zh-CN.ts +++ b/apps/desktop/src/i18n/locales/zh-CN.ts @@ -1430,6 +1430,11 @@ export default { appLayoutSeparatedDescription: "面板分隔更清晰,标签页为块状样式。", appLayoutClassic: "经典紧凑", appLayoutClassicDescription: "使用连续标签栏和更紧凑的面板标题。", + iconTheme: "图标与 Logo 样式", + iconThemeDefault: "默认", + iconThemeDefaultDescription: "使用彩色 DBX 应用图标。", + iconThemeBlack: "黑色", + iconThemeBlackDescription: "窗口、托盘和 Logo 都使用黑色 DBX 标识。", showTrayIcon: "显示系统托盘/菜单栏图标", showTrayIconDescription: "关闭后不显示图标,但关闭窗口仍会像之前一样隐藏到后台。", dataGridDisplay: "数据表格显示", diff --git a/apps/desktop/src/lib/http.ts b/apps/desktop/src/lib/http.ts index c03d83f73..5a36f0543 100644 --- a/apps/desktop/src/lib/http.ts +++ b/apps/desktop/src/lib/http.ts @@ -789,7 +789,7 @@ export async function loadAiConfig(): Promise { } export async function loadDesktopSettings(): Promise { - return { show_tray_icon: true }; + return { show_tray_icon: true, icon_theme: "default" }; } export async function saveDesktopSettings(_settings: DesktopSettings): Promise { diff --git a/apps/desktop/src/lib/tauri.ts b/apps/desktop/src/lib/tauri.ts index 01a2604fa..a6dfa05ac 100644 --- a/apps/desktop/src/lib/tauri.ts +++ b/apps/desktop/src/lib/tauri.ts @@ -97,6 +97,7 @@ export interface DriverStoreUsage { export interface DesktopSettings { show_tray_icon: boolean; + icon_theme: "default" | "black"; } export interface WebDavConfig { diff --git a/apps/desktop/src/stores/settingsStore.ts b/apps/desktop/src/stores/settingsStore.ts index 51c7b8302..8152cd884 100644 --- a/apps/desktop/src/stores/settingsStore.ts +++ b/apps/desktop/src/stores/settingsStore.ts @@ -38,12 +38,24 @@ export interface AiConfig { export interface DesktopSettings { show_tray_icon: boolean; + icon_theme: DesktopIconTheme; } +export type DesktopIconTheme = "default" | "black"; + export const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = { show_tray_icon: true, + icon_theme: "default", }; +function normalizeDesktopSettings(settings: Partial | null | undefined): DesktopSettings { + const iconTheme = settings?.icon_theme === "black" ? "black" : DEFAULT_DESKTOP_SETTINGS.icon_theme; + return { + show_tray_icon: settings?.show_tray_icon ?? DEFAULT_DESKTOP_SETTINGS.show_tray_icon, + icon_theme: iconTheme, + }; +} + export interface AiProviderPreset extends Omit { label: string; iconSlug?: string; @@ -415,7 +427,7 @@ export const useSettingsStore = defineStore("settings", () => { async function initDesktopSettings() { if (isDesktopSettingsLoaded.value) return; - desktopSettings.value = await api.loadDesktopSettings().catch(() => ({ ...DEFAULT_DESKTOP_SETTINGS })); + desktopSettings.value = normalizeDesktopSettings(await api.loadDesktopSettings().catch(() => null)); isDesktopSettingsLoaded.value = true; } @@ -425,9 +437,9 @@ export const useSettingsStore = defineStore("settings", () => { ...desktopSettings.value, ...partial, }; - desktopSettings.value = next; + desktopSettings.value = normalizeDesktopSettings(next); try { - await api.saveDesktopSettings(next); + await api.saveDesktopSettings(desktopSettings.value); } catch (error) { desktopSettings.value = previous; throw error; diff --git a/crates/dbx-core/src/storage.rs b/crates/dbx-core/src/storage.rs index 8bf30874e..a0d040a0b 100644 --- a/crates/dbx-core/src/storage.rs +++ b/crates/dbx-core/src/storage.rs @@ -18,11 +18,28 @@ pub struct Storage { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct DesktopSettings { pub show_tray_icon: bool, + pub icon_theme: DesktopIconTheme, } impl Default for DesktopSettings { fn default() -> Self { - Self { show_tray_icon: true } + Self { show_tray_icon: true, icon_theme: DesktopIconTheme::Default } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum DesktopIconTheme { + Default, + Black, +} + +impl DesktopIconTheme { + fn from_settings_value(value: Option<&serde_json::Value>) -> Self { + match value.and_then(|value| value.as_str()) { + Some("black") => Self::Black, + _ => Self::Default, + } } } @@ -326,6 +343,10 @@ impl Storage { let mut settings = self.load_app_settings_json().await?; settings.remove("run_in_background"); settings.insert("show_tray_icon".to_string(), serde_json::Value::Bool(desktop_settings.show_tray_icon)); + settings.insert( + "icon_theme".to_string(), + serde_json::to_value(desktop_settings.icon_theme).map_err(|e| e.to_string())?, + ); self.save_app_settings_json(&settings).await } @@ -337,6 +358,7 @@ impl Storage { .and_then(|value| value.as_bool()) .or_else(|| settings.get("run_in_background").and_then(|value| value.as_bool())) .unwrap_or_else(|| DesktopSettings::default().show_tray_icon), + icon_theme: DesktopIconTheme::from_settings_value(settings.get("icon_theme")), }) } @@ -1063,7 +1085,7 @@ fn map_from_sql_err(err: serde_json::Error) -> rusqlite::Error { #[cfg(test)] mod tests { - use super::{DesktopSettings, Storage}; + use super::{DesktopIconTheme, DesktopSettings, Storage}; use std::time::{SystemTime, UNIX_EPOCH}; fn temp_db_path(name: &str) -> std::path::PathBuf { @@ -1076,7 +1098,7 @@ mod tests { let path = temp_db_path("desktop-settings-default"); let storage = Storage::open(&path).await.unwrap(); - assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings { show_tray_icon: true }); + assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings::default()); } #[tokio::test] @@ -1087,7 +1109,10 @@ mod tests { settings.insert("run_in_background".to_string(), serde_json::Value::Bool(false)); storage.save_app_settings_json(&settings).await.unwrap(); - assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings { show_tray_icon: false }); + assert_eq!( + storage.load_desktop_settings().await.unwrap(), + DesktopSettings { show_tray_icon: false, ..DesktopSettings::default() } + ); } #[tokio::test] @@ -1096,10 +1121,16 @@ mod tests { let storage = Storage::open(&path).await.unwrap(); storage.save_password_hash("hash-1").await.unwrap(); - storage.save_desktop_settings(&DesktopSettings { show_tray_icon: false }).await.unwrap(); + storage + .save_desktop_settings(&DesktopSettings { show_tray_icon: false, icon_theme: DesktopIconTheme::Black }) + .await + .unwrap(); assert_eq!(storage.load_password_hash().await.unwrap(), Some("hash-1".to_string())); - assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings { show_tray_icon: false }); + assert_eq!( + storage.load_desktop_settings().await.unwrap(), + DesktopSettings { show_tray_icon: false, icon_theme: DesktopIconTheme::Black } + ); } #[tokio::test] @@ -1110,11 +1141,18 @@ mod tests { settings.insert("run_in_background".to_string(), serde_json::Value::Bool(false)); storage.save_app_settings_json(&settings).await.unwrap(); - storage.save_desktop_settings(&DesktopSettings { show_tray_icon: true }).await.unwrap(); + storage + .save_desktop_settings(&DesktopSettings { + icon_theme: DesktopIconTheme::Black, + ..DesktopSettings::default() + }) + .await + .unwrap(); let settings = storage.load_app_settings_json().await.unwrap(); assert_eq!(settings.get("run_in_background"), None); assert_eq!(settings.get("show_tray_icon").and_then(|value| value.as_bool()), Some(true)); + assert_eq!(settings.get("icon_theme").and_then(|value| value.as_str()), Some("black")); } #[tokio::test] @@ -1122,11 +1160,17 @@ mod tests { let path = temp_db_path("password-preserve-desktop-settings"); let storage = Storage::open(&path).await.unwrap(); - storage.save_desktop_settings(&DesktopSettings { show_tray_icon: false }).await.unwrap(); + storage + .save_desktop_settings(&DesktopSettings { show_tray_icon: false, icon_theme: DesktopIconTheme::Black }) + .await + .unwrap(); storage.save_password_hash("hash-2").await.unwrap(); assert_eq!(storage.load_password_hash().await.unwrap(), Some("hash-2".to_string())); - assert_eq!(storage.load_desktop_settings().await.unwrap(), DesktopSettings { show_tray_icon: false }); + assert_eq!( + storage.load_desktop_settings().await.unwrap(), + DesktopSettings { show_tray_icon: false, icon_theme: DesktopIconTheme::Black } + ); } #[tokio::test] diff --git a/src-tauri/icons/icon-black.png b/src-tauri/icons/icon-black.png new file mode 100644 index 000000000..c016b386b Binary files /dev/null and b/src-tauri/icons/icon-black.png differ diff --git a/src-tauri/src/commands/app_settings.rs b/src-tauri/src/commands/app_settings.rs index 28a003167..3a4144009 100644 --- a/src-tauri/src/commands/app_settings.rs +++ b/src-tauri/src/commands/app_settings.rs @@ -4,7 +4,7 @@ use dbx_core::storage::DesktopSettings; use tauri::{AppHandle, State}; use super::connection::AppState; -use crate::apply_desktop_tray_preference; +use crate::apply_desktop_settings; #[tauri::command] pub async fn load_desktop_settings(state: State<'_, Arc>) -> Result { @@ -18,8 +18,8 @@ pub async fn save_desktop_settings( settings: DesktopSettings, ) -> Result<(), String> { state.storage.save_desktop_settings(&settings).await?; - if let Err(err) = apply_desktop_tray_preference(&app, settings.show_tray_icon) { - eprintln!("Failed to apply desktop tray preference: {err}"); + if let Err(err) = apply_desktop_settings(&app, &settings) { + eprintln!("Failed to apply desktop settings: {err}"); } Ok(()) } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f04cb46b5..2d2e481b5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,7 +5,7 @@ mod models; mod window_state_guard; use commands::connection::AppState; -use dbx_core::storage::Storage; +use dbx_core::storage::{DesktopIconTheme, DesktopSettings, Storage}; use std::sync::Arc; use std::time::Instant; #[cfg(target_os = "macos")] @@ -21,6 +21,7 @@ use tauri_plugin_deep_link::DeepLinkExt; const DESKTOP_TRAY_ID: &str = "main-tray"; #[cfg(target_os = "macos")] const MACOS_TRAY_ICON: tauri::image::Image<'_> = tauri::include_image!("icons/tray-macos-template.png"); +const BLACK_APP_ICON: tauri::image::Image<'_> = tauri::include_image!("icons/icon-black.png"); fn should_hide_window_on_close(target_os: &str) -> bool { matches!(target_os, "macos" | "windows") @@ -54,15 +55,35 @@ fn open_connection_deep_links(app: &tauri::AppHandle, links: Vec) { } #[cfg_attr(not(any(target_os = "macos", target_os = "windows")), allow(dead_code))] -fn setup_desktop_tray>(manager: &M) -> tauri::Result<()> { +fn setup_desktop_tray>( + manager: &M, + icon_theme: DesktopIconTheme, +) -> tauri::Result<()> { let menu = MenuBuilder::new(manager).text("show", "Show DBX").separator().text("quit", "Quit DBX").build()?; let mut tray = TrayIconBuilder::::with_id(DESKTOP_TRAY_ID).tooltip("DBX").menu(&menu).show_menu_on_left_click(false); #[cfg(target_os = "macos")] { - tray = tray.icon(MACOS_TRAY_ICON).icon_as_template(true); + match icon_theme { + DesktopIconTheme::Default => { + tray = tray.icon(MACOS_TRAY_ICON).icon_as_template(true); + } + DesktopIconTheme::Black => { + tray = tray.icon(BLACK_APP_ICON).icon_as_template(false); + } + } } - #[cfg(not(target_os = "macos"))] + #[cfg(target_os = "windows")] + { + let icon = match icon_theme { + DesktopIconTheme::Default => manager.app_handle().default_window_icon().cloned(), + DesktopIconTheme::Black => Some(BLACK_APP_ICON), + }; + if let Some(icon) = icon { + tray = tray.icon(icon); + } + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] { if let Some(icon) = manager.app_handle().default_window_icon().cloned() { tray = tray.icon(icon); @@ -86,12 +107,59 @@ fn setup_desktop_tray>(manager: &M) -> tauri::R Ok(()) } -pub(crate) fn apply_desktop_tray_preference(app: &tauri::AppHandle, show_tray_icon: bool) -> tauri::Result<()> { +fn apply_desktop_icon_theme(app: &tauri::AppHandle, icon_theme: DesktopIconTheme) -> tauri::Result<()> { + if let Some(window) = app.get_webview_window("main") { + match icon_theme { + DesktopIconTheme::Default => { + if let Some(icon) = app.default_window_icon().cloned() { + window.set_icon(icon)?; + } + } + DesktopIconTheme::Black => window.set_icon(BLACK_APP_ICON)?, + } + } + Ok(()) +} + +fn apply_desktop_tray_icon_theme(app: &tauri::AppHandle, icon_theme: DesktopIconTheme) -> tauri::Result<()> { + if let Some(tray) = app.tray_by_id(DESKTOP_TRAY_ID) { + #[cfg(target_os = "macos")] + { + match icon_theme { + DesktopIconTheme::Default => { + tray.set_icon(Some(MACOS_TRAY_ICON))?; + tray.set_icon_as_template(true)?; + } + DesktopIconTheme::Black => { + tray.set_icon(Some(BLACK_APP_ICON))?; + tray.set_icon_as_template(false)?; + } + } + } + #[cfg(target_os = "windows")] + { + let icon = match icon_theme { + DesktopIconTheme::Default => app.default_window_icon().cloned(), + DesktopIconTheme::Black => Some(BLACK_APP_ICON), + }; + tray.set_icon(icon)?; + } + #[cfg(not(any(target_os = "macos", target_os = "windows")))] + { + let _ = (tray, icon_theme); + } + } + Ok(()) +} + +pub(crate) fn apply_desktop_settings(app: &tauri::AppHandle, desktop_settings: &DesktopSettings) -> tauri::Result<()> { + apply_desktop_icon_theme(app, desktop_settings.icon_theme)?; if matches!(std::env::consts::OS, "macos" | "windows") { if let Some(tray) = app.tray_by_id(DESKTOP_TRAY_ID) { - tray.set_visible(show_tray_icon)?; - } else if show_tray_icon { - setup_desktop_tray(app)?; + tray.set_visible(desktop_settings.show_tray_icon)?; + apply_desktop_tray_icon_theme(app, desktop_settings.icon_theme)?; + } else if desktop_settings.show_tray_icon { + setup_desktop_tray(app, desktop_settings.icon_theme)?; } } Ok(()) @@ -121,7 +189,7 @@ mod tests { assert!(!should_setup_desktop_tray("linux", true)); let source = include_str!("lib.rs"); assert!(source.contains( - "if should_setup_desktop_tray(std::env::consts::OS, desktop_settings.show_tray_icon) {\n setup_desktop_tray(app)?;" + "if should_setup_desktop_tray(std::env::consts::OS, desktop_settings.show_tray_icon) {\n setup_desktop_tray(app, desktop_settings.icon_theme)?;" )); } @@ -133,10 +201,18 @@ mod tests { assert!(!source.contains(remove_call)); } + #[test] + fn can_apply_black_logo_icon_theme() { + let source = include_str!("lib.rs"); + assert!(source.contains("const BLACK_APP_ICON")); + assert!(source.contains("DesktopIconTheme::Black => window.set_icon(BLACK_APP_ICON)?")); + assert!(source.contains("DesktopIconTheme::Black => Some(BLACK_APP_ICON)")); + } + #[test] fn desktop_settings_save_treats_runtime_tray_update_as_best_effort() { let source = include_str!("commands/app_settings.rs"); - assert!(source.contains("if let Err(err) = apply_desktop_tray_preference")); + assert!(source.contains("if let Err(err) = apply_desktop_settings")); assert!(!source.contains("map_err(|err| err.to_string())")); } @@ -233,8 +309,9 @@ pub fn run() { } } if should_setup_desktop_tray(std::env::consts::OS, desktop_settings.show_tray_icon) { - setup_desktop_tray(app)?; + setup_desktop_tray(app, desktop_settings.icon_theme)?; } + apply_desktop_icon_theme(app.handle(), desktop_settings.icon_theme)?; window_state_guard::enforce_main_window_bounds(app.handle()); if should_show_main_window_after_setup() { show_main_window(app.handle());