From 3e65d3a9b5edcc5c1353686fe00b56d266db4e6e Mon Sep 17 00:00:00 2001 From: Kenneth Chew <79120643+kthchew@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:28:28 -0400 Subject: [PATCH 1/2] Apply selected style to window elements on macOS Qt doesn't apply the proper style to elements such as the title bar or text shadows, so this must be done in native code. Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com> --- launcher/CMakeLists.txt | 7 +++ launcher/ui/themes/ThemeManager.cpp | 8 ++++ launcher/ui/themes/ThemeManager.h | 9 ++++ launcher/ui/themes/ThemeManager.mm | 67 +++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 launcher/ui/themes/ThemeManager.mm diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index ff6a9ab2a..5c23aae0d 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1178,6 +1178,13 @@ SET(LAUNCHER_SOURCES ui/instanceview/VisualGroup.h ) +if (APPLE) + set(LAUNCHER_SOURCES + ${LAUNCHER_SOURCES} + ui/themes/ThemeManager.mm + ) +endif() + if (NOT Apple) set(LAUNCHER_SOURCES ${LAUNCHER_SOURCES} diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index 30a1fe7be..c1af63dc8 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -174,6 +174,13 @@ void ThemeManager::initializeWidgets() themeDebugLog() << "<> Widget themes initialized."; } +#ifndef Q_OS_MACOS +void ThemeManager::setTitlebarColorOnMac(WId windowId, QColor color) +{} +void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) +{} +#endif + QList ThemeManager::getValidIconThemes() { QList ret; @@ -247,6 +254,7 @@ void ThemeManager::setApplicationTheme(const QString& name, bool initial) auto& theme = themeIter->second; themeDebugLog() << "applying theme" << theme->name(); theme->apply(initial); + setTitlebarColorOfAllWindowsOnMac(qApp->palette().window().color()); m_logColors = theme->logColorScheme(); } else { diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index 8de7562d1..dd33523d8 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -81,6 +81,15 @@ class ThemeManager { void initializeIcons(); void initializeWidgets(); + // On non-Mac systems, this is a no-op. + void setTitlebarColorOnMac(WId windowId, QColor color); + // This also will set the titlebar color of newly opened windows after this method is called. + // On non-Mac systems, this is a no-op. + void setTitlebarColorOfAllWindowsOnMac(QColor color); +#ifdef Q_OS_MACOS + NSObject* m_windowTitlebarObserver = nullptr; +#endif + const QStringList builtinIcons{ "pe_colored", "pe_light", "pe_dark", "pe_blue", "breeze_light", "breeze_dark", "OSX", "iOS", "flat", "flat_white", "multimc" }; }; diff --git a/launcher/ui/themes/ThemeManager.mm b/launcher/ui/themes/ThemeManager.mm new file mode 100644 index 000000000..21b3d8e35 --- /dev/null +++ b/launcher/ui/themes/ThemeManager.mm @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2025 Kenneth Chew <79120643+kthchew@users.noreply.github.com> + * + * 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, version 3. + * + * 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 . + */ + +#include "ThemeManager.h" + +#include + +void ThemeManager::setTitlebarColorOnMac(WId windowId, QColor color) +{ + if (windowId == 0) { + return; + } + + NSView* view = (NSView*)windowId; + NSWindow* window = [view window]; + window.titlebarAppearsTransparent = YES; + window.backgroundColor = [NSColor colorWithRed:color.redF() green:color.greenF() blue:color.blueF() alpha:color.alphaF()]; + + // Unfortunately there seems to be no easy way to set the titlebar text color. + // The closest we can do without dubious hacks is set the dark/light mode state based on the brightness of the + // background color, which should at least make the text readable even if we can't use the theme's text color. + // It's a good idea to set this anyway since it also affects some other UI elements like text shadows (PrismLauncher#3825). + if (color.lightnessF() < 0.5) { + window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; + } else { + window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; + } +} + +void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) +{ + NSArray* windows = [NSApp windows]; + for (NSWindow* window : windows) { + setTitlebarColorOnMac((WId)window.contentView, color); + } + + // We want to change the titlebar color of newly opened windows as well. + // There's no notification for when a new window is opened, but we can set the color when a window switches + // from occluded to visible, which also fires on open. + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + if (m_windowTitlebarObserver) { + [center removeObserver:m_windowTitlebarObserver]; + m_windowTitlebarObserver = nil; + } + m_windowTitlebarObserver = [center addObserverForName:NSWindowDidChangeOcclusionStateNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* notification) { + NSWindow* window = notification.object; + setTitlebarColorOnMac((WId)window.contentView, color); + }]; +} From 677a7d7a052f2f200eac28ddf822da493d1e2ac1 Mon Sep 17 00:00:00 2001 From: Kenneth Chew <79120643+kthchew@users.noreply.github.com> Date: Wed, 16 Jul 2025 01:10:05 -0400 Subject: [PATCH 2/2] Unregister window observer before theme manager is deallocated Technically this probably isn't actually necessary since ThemeManager looks like it should remain allocated until the program quits, but... Signed-off-by: Kenneth Chew <79120643+kthchew@users.noreply.github.com> --- launcher/ui/themes/ThemeManager.cpp | 7 +++++++ launcher/ui/themes/ThemeManager.h | 3 +++ launcher/ui/themes/ThemeManager.mm | 14 ++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp index c1af63dc8..a7076da5a 100644 --- a/launcher/ui/themes/ThemeManager.cpp +++ b/launcher/ui/themes/ThemeManager.cpp @@ -50,6 +50,11 @@ ThemeManager::ThemeManager() initializeCatPacks(); } +ThemeManager::~ThemeManager() +{ + stopSettingNewWindowColorsOnMac(); +} + /// @brief Adds the Theme to the list of themes /// @param theme The Theme to add /// @return Theme ID @@ -179,6 +184,8 @@ void ThemeManager::setTitlebarColorOnMac(WId windowId, QColor color) {} void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) {} +void ThemeManager::stopSettingNewWindowColorsOnMac() +{} #endif QList ThemeManager::getValidIconThemes() diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h index dd33523d8..8baa88627 100644 --- a/launcher/ui/themes/ThemeManager.h +++ b/launcher/ui/themes/ThemeManager.h @@ -39,6 +39,7 @@ inline auto themeWarningLog() class ThemeManager { public: ThemeManager(); + ~ThemeManager(); QList getValidIconThemes(); QList getValidApplicationThemes(); @@ -86,6 +87,8 @@ class ThemeManager { // This also will set the titlebar color of newly opened windows after this method is called. // On non-Mac systems, this is a no-op. void setTitlebarColorOfAllWindowsOnMac(QColor color); + // On non-Mac systems, this is a no-op. + void stopSettingNewWindowColorsOnMac(); #ifdef Q_OS_MACOS NSObject* m_windowTitlebarObserver = nullptr; #endif diff --git a/launcher/ui/themes/ThemeManager.mm b/launcher/ui/themes/ThemeManager.mm index 21b3d8e35..d9fc291b6 100644 --- a/launcher/ui/themes/ThemeManager.mm +++ b/launcher/ui/themes/ThemeManager.mm @@ -53,10 +53,7 @@ void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) // There's no notification for when a new window is opened, but we can set the color when a window switches // from occluded to visible, which also fires on open. NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - if (m_windowTitlebarObserver) { - [center removeObserver:m_windowTitlebarObserver]; - m_windowTitlebarObserver = nil; - } + stopSettingNewWindowColorsOnMac(); m_windowTitlebarObserver = [center addObserverForName:NSWindowDidChangeOcclusionStateNotification object:nil queue:[NSOperationQueue mainQueue] @@ -65,3 +62,12 @@ void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) setTitlebarColorOnMac((WId)window.contentView, color); }]; } + +void ThemeManager::stopSettingNewWindowColorsOnMac() +{ + if (m_windowTitlebarObserver) { + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center removeObserver:m_windowTitlebarObserver]; + m_windowTitlebarObserver = nil; + } +}