diff --git a/assets/Core/BrowserWindow/app-icon-dark-transparent.png b/assets/Core/BrowserWindow/app-icon-dark-transparent.png new file mode 100644 index 00000000..e5ef0e3c Binary files /dev/null and b/assets/Core/BrowserWindow/app-icon-dark-transparent.png differ diff --git a/assets/Core/BrowserWindow/app-icon-dark.png b/assets/Core/BrowserWindow/app-icon-dark.png new file mode 100644 index 00000000..8c53bbdf Binary files /dev/null and b/assets/Core/BrowserWindow/app-icon-dark.png differ diff --git a/assets/Core/BrowserWindow/app-icon-light-transparent.png b/assets/Core/BrowserWindow/app-icon-light-transparent.png new file mode 100644 index 00000000..d3bb74f5 Binary files /dev/null and b/assets/Core/BrowserWindow/app-icon-light-transparent.png differ diff --git a/assets/Core/BrowserWindow/app-icon-light.png b/assets/Core/BrowserWindow/app-icon-light.png new file mode 100644 index 00000000..30537d9f Binary files /dev/null and b/assets/Core/BrowserWindow/app-icon-light.png differ diff --git a/src/main/Core/BrowserWindow/BrowserWindowModule.ts b/src/main/Core/BrowserWindow/BrowserWindowModule.ts index 3f48e3cd..2ffe4c49 100644 --- a/src/main/Core/BrowserWindow/BrowserWindowModule.ts +++ b/src/main/Core/BrowserWindow/BrowserWindowModule.ts @@ -6,6 +6,7 @@ import type { SearchResultItemAction } from "@common/Core"; import type { BrowserWindow, BrowserWindowConstructorOptions } from "electron"; import { join } from "path"; import { createBrowserWindow } from "./createBrowserWindow"; +import { getAppIconFilePath } from "./getAppIconFilePath"; import { getBackgroundMaterial } from "./getBackgroundMaterial"; import { getVibrancy } from "./getVibrancy"; import { openAndFocusBrowserWindow } from "./openAndFocusBrowserWindow"; @@ -15,11 +16,22 @@ import { toggleBrowserWindow } from "./toggleBrowserWindow"; export class BrowserWindowModule { public static async bootstrap(dependencyRegistry: DependencyRegistry) { const eventEmitter = dependencyRegistry.get("EventEmitter"); + const nativeTheme = dependencyRegistry.get("NativeTheme"); const browserWindow = createBrowserWindow(dependencyRegistry); eventEmitter.emitEvent("browserWindowCreated", { browserWindow }); + nativeTheme.addListener("updated", () => + browserWindow.setIcon( + getAppIconFilePath( + dependencyRegistry.get("NativeTheme"), + dependencyRegistry.get("AssetPathResolver"), + dependencyRegistry.get("OperatingSystem"), + ), + ), + ); + BrowserWindowModule.registerBrowserWindowEventListeners(browserWindow, dependencyRegistry); BrowserWindowModule.registerEvents(browserWindow, dependencyRegistry); await BrowserWindowModule.loadFileOrUrl(browserWindow, dependencyRegistry); diff --git a/src/main/Core/BrowserWindow/createBrowserWindow.ts b/src/main/Core/BrowserWindow/createBrowserWindow.ts index b7c3ba65..1b0582a5 100644 --- a/src/main/Core/BrowserWindow/createBrowserWindow.ts +++ b/src/main/Core/BrowserWindow/createBrowserWindow.ts @@ -3,6 +3,7 @@ import type { DependencyRegistry } from "@Core/DependencyRegistry"; import type { OperatingSystem } from "@common/Core"; import { BrowserWindow, type BrowserWindowConstructorOptions } from "electron"; import { join } from "path"; +import { getAppIconFilePath } from "./getAppIconFilePath"; import { getBackgroundMaterial } from "./getBackgroundMaterial"; import { getVibrancy } from "./getVibrancy"; @@ -28,6 +29,11 @@ export const createBrowserWindow = (dependencyRegistry: DependencyRegistry { diff --git a/src/main/Core/BrowserWindow/getAppIconFilePath.test.ts b/src/main/Core/BrowserWindow/getAppIconFilePath.test.ts new file mode 100644 index 00000000..4479dac3 --- /dev/null +++ b/src/main/Core/BrowserWindow/getAppIconFilePath.test.ts @@ -0,0 +1,60 @@ +import type { AssetPathResolver } from "@Core/AssetPathResolver"; +import type { NativeTheme } from "electron"; +import { describe, expect, it, vi } from "vitest"; +import { getAppIconFilePath } from "./getAppIconFilePath"; + +describe(getAppIconFilePath, () => { + it("it should return the correct app icon file path on Windows' dark theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("windows-dark-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: true }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "Windows")).toBe("windows-dark-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-dark-transparent.png"); + }); + + it("it should return the correct app icon file path on Windows' light theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("windows-light-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: false }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "Windows")).toBe("windows-light-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-light-transparent.png"); + }); + + it("it should return the correct app icon file path on macOS' dark theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("macos-dark-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: true }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "macOS")).toBe("macos-dark-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-dark.png"); + }); + + it("it should return the correct app icon file path on macOS' light theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("macos-light-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: false }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "macOS")).toBe("macos-light-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-light.png"); + }); + + it("it should return the correct app icon file path on Linux' dark theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("linux-dark-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: true }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "macOS")).toBe("linux-dark-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-dark.png"); + }); + + it("it should return the correct app icon file path on Linux' light theme", () => { + const getModuleAssetPathMock = vi.fn().mockReturnValue("linux-light-theme-icon.png"); + const assetPathResolver = { getModuleAssetPath: (m, a) => getModuleAssetPathMock(m, a) }; + const nativeTheme = { shouldUseDarkColors: false }; + + expect(getAppIconFilePath(nativeTheme, assetPathResolver, "macOS")).toBe("linux-light-theme-icon.png"); + expect(getModuleAssetPathMock).toHaveBeenCalledWith("BrowserWindow", "app-icon-light.png"); + }); +}); diff --git a/src/main/Core/BrowserWindow/getAppIconFilePath.ts b/src/main/Core/BrowserWindow/getAppIconFilePath.ts new file mode 100644 index 00000000..67e36cf4 --- /dev/null +++ b/src/main/Core/BrowserWindow/getAppIconFilePath.ts @@ -0,0 +1,21 @@ +import type { AssetPathResolver } from "@Core/AssetPathResolver"; +import type { OperatingSystem } from "@common/Core"; +import type { NativeTheme } from "electron"; + +export const getAppIconFilePath = ( + nativeTheme: NativeTheme, + assetPathResolver: AssetPathResolver, + operatingSystem: OperatingSystem, +): string => { + const fileNames: Record = { + Linux: { dark: "app-icon-dark.png", light: "app-icon-light.png" }, + macOS: { dark: "app-icon-dark.png", light: "app-icon-light.png" }, + Windows: { dark: "app-icon-dark-transparent.png", light: "app-icon-light-transparent.png" }, + }; + + const filename = nativeTheme.shouldUseDarkColors + ? fileNames[operatingSystem].dark + : fileNames[operatingSystem].light; + + return assetPathResolver.getModuleAssetPath("BrowserWindow", filename); +};