diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java b/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java index 7f13f54e31..d3202772c7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/EntryPoint.java @@ -21,8 +21,7 @@ import org.jackhuang.hmcl.util.SelfDependencyPatcher; import org.jackhuang.hmcl.util.SwingUtils; import org.jackhuang.hmcl.java.JavaRuntime; -import org.jackhuang.hmcl.util.io.FileUtils; -import org.jackhuang.hmcl.util.io.JarUtils; +import org.jackhuang.hmcl.util.TaskbarIconManager; import org.jackhuang.hmcl.util.platform.OperatingSystem; import java.io.IOException; @@ -30,7 +29,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.file.Files; -import java.nio.file.Path; import java.util.concurrent.CancellationException; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -52,8 +50,8 @@ public static void main(String[] args) { setupJavaFXVMOptions(); checkDirectoryPath(); - if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS && !isInsideMacAppBundle()) - initIcon(); + if (!OperatingSystem.isInsideMacAppBundle()) + TaskbarIconManager.setIcon("/assets/img/icon-mac.png"); checkJavaFX(); verifyJavaFX(); @@ -162,36 +160,6 @@ private static void createHMCLDirectories() { } } - private static boolean isInsideMacAppBundle() { - Path thisJar = JarUtils.thisJarPath(); - if (thisJar == null) - return false; - - for (Path current = thisJar.getParent(); - current != null && current.getParent() != null; - current = current.getParent() - ) { - if ("Contents".equals(FileUtils.getName(current)) - && FileUtils.getName(current.getParent()).endsWith(".app") - && Files.exists(current.resolve("Info.plist")) - ) { - return true; - } - } - return false; - } - - private static void initIcon() { - try { - if (java.awt.Taskbar.isTaskbarSupported()) { - var image = java.awt.Toolkit.getDefaultToolkit().getImage(EntryPoint.class.getResource("/assets/img/icon-mac.png")); - java.awt.Taskbar.getTaskbar().setIconImage(image); - } - } catch (Throwable e) { - LOG.warning("Failed to set application icon", e); - } - } - private static void checkDirectoryPath() { String currentDir = System.getProperty("user.dir", ""); if (currentDir.contains("!")) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index 84e667ce90..d2829e710e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -60,6 +60,7 @@ import org.jackhuang.hmcl.task.CacheFileTask; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.theme.Themes; import org.jackhuang.hmcl.ui.animation.AnimationUtils; import org.jackhuang.hmcl.ui.image.ImageLoader; import org.jackhuang.hmcl.ui.image.ImageUtils; @@ -1085,15 +1086,32 @@ public void invalidated(Observable observable) { } public static void setIcon(Stage stage) { - String icon; - if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { - icon = "/assets/img/icon.png"; - } else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { - icon = "/assets/img/icon-mac.png"; - } else { - icon = "/assets/img/icon@4x.png"; + Runnable updateIcon = () -> { + String iconPath = switch (OperatingSystem.CURRENT_OS) { + case WINDOWS -> "/assets/img/icon.png"; + case MACOS -> Themes.darkModeProperty().get() + ? "/assets/img/icon-mac-dark.png" + : "/assets/img/icon-mac.png"; + default -> "/assets/img/icon@4x.png"; + }; + + Image fxImage = newBuiltinImage(iconPath); + if (fxImage != null) { + stage.getIcons().setAll(fxImage); + } + + if (!OperatingSystem.isInsideMacAppBundle()) { + TaskbarIconManager.setIcon(iconPath); + } + }; + + Platform.runLater(updateIcon); + + if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { + Themes.darkModeProperty().addListener((obs, oldDark, newDark) -> + Platform.runLater(updateIcon) + ); } - stage.getIcons().add(newBuiltinImage(icon)); } public static Image loadImage(Path path) throws Exception { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/TaskbarIconManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/TaskbarIconManager.java new file mode 100644 index 0000000000..d87db113aa --- /dev/null +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/TaskbarIconManager.java @@ -0,0 +1,46 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.jackhuang.hmcl.util; + +import java.net.URL; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +public final class TaskbarIconManager { + public static void setIcon(String iconPath) { + if (!java.awt.Taskbar.isTaskbarSupported()) { + return; + } + + try { + URL resource = TaskbarIconManager.class.getResource(iconPath); + if (resource == null) { + LOG.warning("Icon resource not found: " + iconPath); + return; + } + + java.awt.Image image = java.awt.Toolkit.getDefaultToolkit().getImage(resource); + java.awt.Taskbar.getTaskbar().setIconImage(image); + } catch (Throwable e) { + LOG.warning("Failed to set AWT taskbar icon: " + iconPath, e); + } + } + + private TaskbarIconManager() {} +} diff --git a/HMCL/src/main/resources/assets/img/icon-mac-dark.png b/HMCL/src/main/resources/assets/img/icon-mac-dark.png new file mode 100644 index 0000000000..336499b188 Binary files /dev/null and b/HMCL/src/main/resources/assets/img/icon-mac-dark.png differ diff --git a/HMCL/src/main/resources/assets/img/icon-mac.png b/HMCL/src/main/resources/assets/img/icon-mac.png index c21203db47..2a30c19b6c 100644 Binary files a/HMCL/src/main/resources/assets/img/icon-mac.png and b/HMCL/src/main/resources/assets/img/icon-mac.png differ diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java index 51f6c2f6fc..cc421cabd1 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/OperatingSystem.java @@ -18,6 +18,8 @@ package org.jackhuang.hmcl.util.platform; import org.jackhuang.hmcl.util.KeyValuePairUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.windows.Kernel32; import org.jackhuang.hmcl.util.platform.windows.WinReg; @@ -282,6 +284,27 @@ public static boolean isWindows7OrLater() { return SYSTEM_VERSION.isAtLeast(OSVersion.WINDOWS_7); } + public static boolean isInsideMacAppBundle() { + if (OperatingSystem.CURRENT_OS != MACOS) return false; + + Path thisJar = JarUtils.thisJarPath(); + if (thisJar == null) + return false; + + for (Path current = thisJar.getParent(); + current != null && current.getParent() != null; + current = current.getParent() + ) { + if ("Contents".equals(FileUtils.getName(current)) + && FileUtils.getName(current.getParent()).endsWith(".app") + && Files.exists(current.resolve("Info.plist")) + ) { + return true; + } + } + return false; + } + public static Path getWorkingDirectory(String folder) { String home = System.getProperty("user.home", "."); switch (OperatingSystem.CURRENT_OS) {