Merge pull request #6034 from keepassxreboot/hotfix/theme-switching-backport

This commit is contained in:
Jonathan White 2021-01-31 15:43:37 -05:00 committed by GitHub
commit 109671900b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 293 additions and 131 deletions

3
.gitattributes vendored
View file

@ -11,3 +11,6 @@ AppImage-Recipe.sh export-ignore
# github-linguist language hints
*.h linguist-language=C++
*.cpp linguist-language=C++
# binary files
*.ai binary

View file

@ -20,6 +20,7 @@
#include <QBitmap>
#include <QDir>
#include <QIconEngine>
#include <QLibrary>
#include <QPainter>
#include <QStyle>
@ -30,6 +31,18 @@
#include "gui/MainWindow.h"
#include "gui/osutils/OSUtils.h"
class AdaptiveIconEngine : public QIconEngine
{
public:
explicit AdaptiveIconEngine(QIcon baseIcon);
void paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) override;
QPixmap pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
QIconEngine* clone() const override;
private:
QIcon m_baseIcon;
};
Resources* Resources::m_instance(nullptr);
QString Resources::dataPath(const QString& name) const
@ -114,45 +127,105 @@ QString Resources::trayIconAppearance() const
return iconAppearance;
}
QIcon Resources::trayIcon()
QIcon Resources::trayIcon(QString style)
{
return trayIconUnlocked();
if (style == "unlocked") {
style.clear();
}
if (!style.isEmpty()) {
style = "-" + style;
}
auto iconApperance = trayIconAppearance();
if (!iconApperance.startsWith("monochrome")) {
return icon(QString("keepassxc%1").arg(style), false);
}
QIcon i;
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
if (osUtils->isStatusBarDark()) {
i = icon(QString("keepassxc-monochrome-light%1").arg(style), false);
} else {
i = icon(QString("keepassxc-monochrome-dark%1").arg(style), false);
}
#else
i = icon(QString("keepassxc-%1%2").arg(iconApperance, style), false);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
// Set as mask to allow the operating system to recolour the tray icon. This may look weird
// if we failed to detect the status bar background colour correctly, but it is certainly
// better than a barely visible icon and even if we did guess correctly, it allows for better
// integration should the system's preferred colours not be 100% black or white.
i.setIsMask(true);
#endif
return i;
}
QIcon Resources::trayIconLocked()
{
auto iconApperance = trayIconAppearance();
if (iconApperance == "monochrome-light") {
return icon("keepassxc-monochrome-light-locked", false);
}
if (iconApperance == "monochrome-dark") {
return icon("keepassxc-monochrome-dark-locked", false);
}
return icon("keepassxc-locked", false);
return trayIcon("locked");
}
QIcon Resources::trayIconUnlocked()
{
auto iconApperance = trayIconAppearance();
return trayIcon("unlocked");
}
if (iconApperance == "monochrome-light") {
return icon("keepassxc-monochrome-light", false);
AdaptiveIconEngine::AdaptiveIconEngine(QIcon baseIcon)
: QIconEngine()
, m_baseIcon(std::move(baseIcon))
{
}
void AdaptiveIconEngine::paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state)
{
// Temporary image canvas to ensure that the background is transparent and alpha blending works.
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
auto scale = painter->device()->devicePixelRatioF();
#else
auto scale = painter->device()->devicePixelRatio();
#endif
QImage img(rect.size() * scale, QImage::Format_ARGB32_Premultiplied);
img.fill(0);
QPainter p(&img);
m_baseIcon.paint(&p, img.rect(), Qt::AlignCenter, mode, state);
if (getMainWindow()) {
QPalette palette = getMainWindow()->palette();
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
if (mode == QIcon::Active) {
p.fillRect(img.rect(), palette.color(QPalette::Active, QPalette::ButtonText));
} else if (mode == QIcon::Selected) {
p.fillRect(img.rect(), palette.color(QPalette::Active, QPalette::HighlightedText));
} else if (mode == QIcon::Disabled) {
p.fillRect(img.rect(), palette.color(QPalette::Disabled, QPalette::WindowText));
} else {
p.fillRect(img.rect(), palette.color(QPalette::Normal, QPalette::WindowText));
}
}
if (iconApperance == "monochrome-dark") {
return icon("keepassxc-monochrome-dark", false);
}
return icon("keepassxc", false);
painter->drawImage(rect, img);
}
QPixmap AdaptiveIconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state)
{
QImage img(size, QImage::Format_ARGB32_Premultiplied);
img.fill(0);
QPainter painter(&img);
paint(&painter, QRect(0, 0, size.width(), size.height()), mode, state);
return QPixmap::fromImage(img, Qt::ImageConversionFlag::NoFormatConversion);
}
QIconEngine* AdaptiveIconEngine::clone() const
{
return new AdaptiveIconEngine(m_baseIcon);
}
QIcon Resources::icon(const QString& name, bool recolor, const QColor& overrideColor)
{
QIcon icon = m_iconCache.value(name);
if (!icon.isNull() && !overrideColor.isValid()) {
return icon;
}
#ifdef Q_OS_LINUX
// Resetting the application theme name before calling QIcon::fromTheme() is required for hacky
// QPA platform themes such as qt5ct, which randomly mess with the configured icon theme.
// If we do not reset the theme name here, it will become empty at some point, causing
@ -161,71 +234,31 @@ QIcon Resources::icon(const QString& name, bool recolor, const QColor& overrideC
// See issue #4963: https://github.com/keepassxreboot/keepassxc/issues/4963
// and qt5ct issue #80: https://sourceforge.net/p/qt5ct/tickets/80/
QIcon::setThemeName("application");
#endif
QString cacheName =
QString("%1:%2:%3").arg(recolor ? "1" : "0", overrideColor.isValid() ? overrideColor.name() : "#", name);
QIcon icon = m_iconCache.value(cacheName);
if (!icon.isNull() && !overrideColor.isValid()) {
return icon;
}
icon = QIcon::fromTheme(name);
if (getMainWindow() && recolor) {
const QRect rect(0, 0, 48, 48);
QImage img = icon.pixmap(rect.width(), rect.height()).toImage();
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
icon = {};
QPainter painter(&img);
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
if (!overrideColor.isValid()) {
QPalette palette = getMainWindow()->palette();
painter.fillRect(rect, palette.color(QPalette::Normal, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
painter.fillRect(rect, palette.color(QPalette::Active, QPalette::ButtonText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Active);
painter.fillRect(rect, palette.color(QPalette::Active, QPalette::HighlightedText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected);
painter.fillRect(rect, palette.color(QPalette::Disabled, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Disabled);
} else {
painter.fillRect(rect, overrideColor);
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
}
if (recolor) {
icon = QIcon(new AdaptiveIconEngine(icon));
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
icon.setIsMask(true);
#endif
}
if (!overrideColor.isValid()) {
m_iconCache.insert(name, icon);
}
m_iconCache.insert(cacheName, icon);
return icon;
}
QIcon Resources::onOffIcon(const QString& name, bool recolor)
QIcon Resources::onOffIcon(const QString& name, bool on, bool recolor)
{
QString cacheName = "onoff/" + name;
QIcon icon = m_iconCache.value(cacheName);
if (!icon.isNull()) {
return icon;
}
const QSize size(48, 48);
QIcon on = Resources::icon(name + "-on", recolor);
icon.addPixmap(on.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::On);
icon.addPixmap(on.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::On);
icon.addPixmap(on.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::On);
QIcon off = Resources::icon(name + "-off", recolor);
icon.addPixmap(off.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::Off);
icon.addPixmap(off.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::Off);
icon.addPixmap(off.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::Off);
m_iconCache.insert(cacheName, icon);
return icon;
return icon(name + (on ? "-on" : "-off"), recolor);
}
Resources::Resources()

View file

@ -31,12 +31,12 @@ public:
QString pluginPath(const QString& name) const;
QString wordlistPath(const QString& name) const;
QIcon applicationIcon();
QIcon trayIcon();
QIcon trayIcon(QString style = "unlocked");
QIcon trayIconLocked();
QIcon trayIconUnlocked();
QString trayIconAppearance() const;
QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid);
QIcon onOffIcon(const QString& name, bool recolor = true);
QIcon onOffIcon(const QString& name, bool on, bool recolor = true);
static Resources* instance();

View file

@ -22,6 +22,7 @@
#include "autotype/AutoType.h"
#include "core/Config.h"
#include "core/Global.h"
#include "core/Resources.h"
#include "gui/MainWindow.h"
#include "gui/osutils/OSUtils.h"
#include "gui/styles/dark/DarkStyle.h"
@ -131,6 +132,12 @@ Application::Application(int& argc, char** argv)
qWarning()
<< QObject::tr("The lock file could not be created. Single-instance mode disabled.").toUtf8().constData();
}
connect(osUtils, &OSUtilsBase::interfaceThemeChanged, this, [this]() {
if (config()->get(Config::GUI_ApplicationTheme).toString() != "classic") {
applyTheme();
}
});
}
Application::~Application()
@ -153,15 +160,15 @@ void Application::applyTheme()
}
#endif
}
QPixmapCache::clear();
if (appTheme == "light") {
setStyle(new LightStyle);
// Workaround Qt 5.15+ bug
setPalette(style()->standardPalette());
auto* s = new LightStyle;
setPalette(s->standardPalette());
setStyle(s);
} else if (appTheme == "dark") {
setStyle(new DarkStyle);
// Workaround Qt 5.15+ bug
setPalette(style()->standardPalette());
auto* s = new DarkStyle;
setPalette(s->standardPalette());
setStyle(s);
m_darkTheme = true;
} else {
// Classic mode, don't check for dark theme on Windows

View file

@ -250,8 +250,12 @@ void ApplicationSettingsWidget::loadSettings()
}
m_generalUi->trayIconAppearance->clear();
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
m_generalUi->trayIconAppearance->addItem(tr("Monochrome"), "monochrome");
#else
m_generalUi->trayIconAppearance->addItem(tr("Monochrome (light)"), "monochrome-light");
m_generalUi->trayIconAppearance->addItem(tr("Monochrome (dark)"), "monochrome-dark");
#endif
m_generalUi->trayIconAppearance->addItem(tr("Colorful"), "colorful");
int trayIconIndex = m_generalUi->trayIconAppearance->findData(resources()->trayIconAppearance());
if (trayIconIndex > 0) {

View file

@ -50,9 +50,9 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
// Entry
m_ui->entryTotpButton->setIcon(resources()->icon("chronometer"));
m_ui->entryCloseButton->setIcon(resources()->icon("dialog-close"));
m_ui->togglePasswordButton->setIcon(resources()->onOffIcon("password-show"));
m_ui->toggleEntryNotesButton->setIcon(resources()->onOffIcon("password-show"));
m_ui->toggleGroupNotesButton->setIcon(resources()->onOffIcon("password-show"));
m_ui->togglePasswordButton->setIcon(resources()->onOffIcon("password-show", true));
m_ui->toggleEntryNotesButton->setIcon(resources()->onOffIcon("password-show", true));
m_ui->toggleGroupNotesButton->setIcon(resources()->onOffIcon("password-show", true));
m_ui->entryAttachmentsWidget->setReadOnly(true);
m_ui->entryAttachmentsWidget->setButtonsVisible(false);
@ -199,16 +199,19 @@ void EntryPreviewWidget::setPasswordVisible(bool state)
} else {
m_ui->entryPasswordLabel->setText(QString("\u25cf").repeated(6));
}
m_ui->togglePasswordButton->setIcon(resources()->onOffIcon("password-show", state));
}
void EntryPreviewWidget::setEntryNotesVisible(bool state)
{
setNotesVisible(m_ui->entryNotesTextEdit, m_currentEntry->notes(), state);
m_ui->toggleEntryNotesButton->setIcon(resources()->onOffIcon("password-show", state));
}
void EntryPreviewWidget::setGroupNotesVisible(bool state)
{
setNotesVisible(m_ui->groupNotesTextEdit, m_currentGroup->notes(), state);
m_ui->toggleGroupNotesButton->setIcon(resources()->onOffIcon("password-show", state));
}
void EntryPreviewWidget::setNotesVisible(QTextEdit* notesWidget, const QString& notes, bool state)

View file

@ -39,6 +39,7 @@
#include "gui/DatabaseWidget.h"
#include "gui/MessageBox.h"
#include "gui/SearchWidget.h"
#include "gui/osutils/OSUtils.h"
#include "keys/CompositeKey.h"
#include "keys/FileKey.h"
#include "keys/PasswordKey.h"
@ -496,6 +497,8 @@ MainWindow::MainWindow()
connect(m_ui->actionOnlineHelp, SIGNAL(triggered()), SLOT(openOnlineHelp()));
connect(m_ui->actionKeyboardShortcuts, SIGNAL(triggered()), SLOT(openKeyboardShortcuts()));
connect(osUtils, &OSUtilsBase::statusbarThemeChanged, this, &MainWindow::updateTrayIcon);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
// Install event filter for empty-area drag
auto* eventFilter = new MainWindowEventFilter(this);
@ -599,6 +602,11 @@ MainWindow::MainWindow()
config()->set(Config::Messages_Qt55CompatibilityWarning, true);
}
#endif
QObject::connect(qApp, SIGNAL(anotherInstanceStarted()), this, SLOT(bringToFront()));
QObject::connect(qApp, SIGNAL(applicationActivated()), this, SLOT(bringToFront()));
QObject::connect(qApp, SIGNAL(openFile(QString)), this, SLOT(openDatabase(QString)));
QObject::connect(qApp, SIGNAL(quitSignalReceived()), this, SLOT(appExit()), Qt::DirectConnection);
}
MainWindow::~MainWindow()
@ -1737,8 +1745,10 @@ void MainWindow::initViewMenu()
connect(themeActions, &QActionGroup::triggered, this, [this, theme](QAction* action) {
config()->set(Config::GUI_ApplicationTheme, action->data());
if (action->data() != theme) {
if ((action->data() == "classic" || theme == "classic") && action->data() != theme) {
restartApp(tr("You must restart the application to apply this setting. Would you like to restart now?"));
} else {
kpxcApp->applyTheme();
}
});

View file

@ -58,7 +58,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
#endif
m_toggleVisibleAction = new QAction(
resources()->icon("password-show-off"),
resources()->onOffIcon("password-show", false),
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
nullptr);
m_toggleVisibleAction->setCheckable(true);
@ -113,7 +113,7 @@ void PasswordEdit::enablePasswordGenerator()
void PasswordEdit::setShowPassword(bool show)
{
setEchoMode(show ? QLineEdit::Normal : QLineEdit::Password);
m_toggleVisibleAction->setIcon(resources()->icon(show ? "password-show-on" : "password-show-off"));
m_toggleVisibleAction->setIcon(resources()->onOffIcon("password-show", show));
m_toggleVisibleAction->setChecked(show);
if (m_repeatPasswordEdit) {

View file

@ -35,6 +35,11 @@ public:
*/
virtual bool isDarkMode() const = 0;
/**
* @return OS task / menu bar is dark.
*/
virtual bool isStatusBarDark() const = 0;
/**
* @return KeePassXC set to launch at system startup (autostart).
*/
@ -50,6 +55,17 @@ public:
*/
virtual bool isCapslockEnabled() = 0;
signals:
/**
* Indicates platform UI theme change (light mode to dark mode).
*/
void interfaceThemeChanged();
/*
* Indicates a change in the tray / statusbar theme.
*/
void statusbarThemeChanged();
protected:
explicit OSUtilsBase(QObject* parent = nullptr);
virtual ~OSUtilsBase();

View file

@ -20,6 +20,7 @@
#define KEEPASSX_APPKIT_H
#include <QObject>
#include <QColor>
#include <unistd.h>
class AppKit : public QObject
@ -37,13 +38,14 @@ public:
bool hideProcess(pid_t pid);
bool isHidden(pid_t pid);
bool isDarkMode();
bool hasDarkMode();
bool isStatusBarDark();
bool enableAccessibility();
bool enableScreenRecording();
void toggleForegroundApp(bool foreground);
signals:
void lockDatabases();
void interfaceThemeChanged();
private:
void* self;

View file

@ -35,6 +35,7 @@
- (bool) hideProcess:(pid_t) pid;
- (bool) isHidden:(pid_t) pid;
- (bool) isDarkMode;
- (bool) isStatusBarDark;
- (void) userSwitchHandler:(NSNotification*) notification;
- (bool) enableAccessibility;
- (bool) enableScreenRecording;

View file

@ -17,7 +17,11 @@
*/
#import "AppKitImpl.h"
#include "AppKit.h"
#import <AppKit/NSStatusBar.h>
#import <AppKit/NSStatusItem.h>
#import <AppKit/NSStatusBarButton.h>
#import <AppKit/NSWorkspace.h>
#import <CoreVideo/CVPixelBuffer.h>
@ -26,17 +30,31 @@
- (id) initWithObject:(AppKit*)appkit
{
self = [super init];
if (self) {
m_appkit = appkit;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didDeactivateApplicationObserver:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:static_cast<id>(self)
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(userSwitchHandler:)
name:NSWorkspaceSessionDidResignActiveNotification
object:nil];
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceThemeChanged:)
name:@"AppleInterfaceThemeChangedNotification"
object:nil];
// Unfortunately, there is no notification for a wallpaper change, which affects
// the status bar colour on macOS Big Sur, but we can at least subscribe to this.
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceThemeChanged:)
name:@"AppleColorPreferencesChangedNotification"
object:nil];
}
return self;
}
@ -54,6 +72,18 @@
}
}
//
// Light / dark theme toggled
//
- (void) interfaceThemeChanged:(NSNotification*) notification
{
Q_UNUSED(notification);
if (m_appkit) {
emit m_appkit->interfaceThemeChanged();
}
}
//
// Get process id of frontmost application (-> keyboard input)
//
@ -108,6 +138,23 @@
&& NSOrderedSame == [style caseInsensitiveCompare:@"dark"] );
}
//
// Get global menu bar theme state
//
- (bool) isStatusBarDark
{
if (@available(macOS 10.17, *)) {
// This is an ugly hack, but I couldn't find a way to access QTrayIcon's NSStatusItem.
NSStatusItem* dummy = [[NSStatusBar systemStatusBar] statusItemWithLength:0];
NSString* appearance = [dummy.button.effectiveAppearance.name lowercaseString];
[[NSStatusBar systemStatusBar] removeStatusItem:dummy];
return [appearance containsString:@"dark"];
}
return [self isDarkMode];
}
//
// Notification for user switch
//
@ -170,7 +217,8 @@
// ------------------------- C++ Trampolines -------------------------
//
AppKit::AppKit(QObject* parent) : QObject(parent)
AppKit::AppKit(QObject* parent)
: QObject(parent)
{
self = [[AppKitImpl alloc] initWithObject:this];
}
@ -178,6 +226,7 @@ AppKit::AppKit(QObject* parent) : QObject(parent)
AppKit::~AppKit()
{
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:static_cast<id>(self)];
[[NSDistributedNotificationCenter defaultCenter] removeObserver:static_cast<id>(self)];
[static_cast<id>(self) dealloc];
}
@ -216,6 +265,12 @@ bool AppKit::isDarkMode()
return [static_cast<id>(self) isDarkMode];
}
bool AppKit::isStatusBarDark()
{
return [static_cast<id>(self) isStatusBarDark];
}
bool AppKit::enableAccessibility()
{
return [static_cast<id>(self) enableAccessibility];

View file

@ -22,6 +22,7 @@
#include <QFile>
#include <QSettings>
#include <QStandardPaths>
#include <QTimer>
#include <CoreGraphics/CGEventSource.h>
@ -33,6 +34,14 @@ MacUtils::MacUtils(QObject* parent)
, m_appkit(new AppKit())
{
connect(m_appkit.data(), SIGNAL(lockDatabases()), SIGNAL(lockDatabases()));
connect(m_appkit.data(), SIGNAL(interfaceThemeChanged()), SIGNAL(interfaceThemeChanged()));
connect(m_appkit.data(), &AppKit::interfaceThemeChanged, this, [this]() {
// Emit with delay, since isStatusBarDark() still returns the old value
// if we call it too fast after a theme change.
QTimer::singleShot(100, [this]() {
emit statusbarThemeChanged();
});
});
}
MacUtils::~MacUtils()
@ -93,6 +102,11 @@ bool MacUtils::isDarkMode() const
return m_appkit->isDarkMode();
}
bool MacUtils::isStatusBarDark() const
{
return m_appkit->isStatusBarDark();
}
QString MacUtils::getLaunchAgentFilename() const
{
auto launchAgentDir = QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));

View file

@ -22,6 +22,7 @@
#include "gui/osutils/OSUtilsBase.h"
#include "AppKit.h"
#include <QColor>
#include <QPointer>
#include <QScopedPointer>
#include <qwindowdefs.h>
@ -34,6 +35,7 @@ public:
static MacUtils* instance();
bool isDarkMode() const override;
bool isStatusBarDark() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override;

View file

@ -62,6 +62,12 @@ bool NixUtils::isDarkMode() const
return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110;
}
bool NixUtils::isStatusBarDark() const
{
// TODO: implement
return isDarkMode();
}
QString NixUtils::getAutostartDesktopFilename(bool createDirs) const
{
QDir autostartDir;

View file

@ -29,6 +29,7 @@ public:
static NixUtils* instance();
bool isDarkMode() const override;
bool isStatusBarDark() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override;

View file

@ -30,6 +30,8 @@ WinUtils* WinUtils::instance()
{
if (!m_instance) {
m_instance = new WinUtils(qApp);
m_instance->m_darkAppThemeActive = m_instance->isDarkMode();
m_instance->m_darkSystemThemeActive = m_instance->isStatusBarDark();
}
return m_instance;
@ -66,14 +68,18 @@ bool WinUtils::DWMEventFilter::nativeEventFilter(const QByteArray& eventType, vo
return false;
}
switch (msg->message) {
case WM_CREATE:
case WM_INITDIALOG: {
if (winUtils()->isDarkMode()) {
// TODO: indicate dark mode support for black title bar
case WM_SETTINGCHANGE:
if (m_instance->m_darkAppThemeActive != m_instance->isDarkMode()) {
m_instance->m_darkAppThemeActive = !m_instance->m_darkAppThemeActive;
emit m_instance->interfaceThemeChanged();
}
if (m_instance->m_darkSystemThemeActive != m_instance->isStatusBarDark()) {
m_instance->m_darkSystemThemeActive = !m_instance->m_darkSystemThemeActive;
emit m_instance->statusbarThemeChanged();
}
break;
}
}
return false;
}
@ -85,6 +91,13 @@ bool WinUtils::isDarkMode() const
return settings.value("AppsUseLightTheme", 1).toInt() == 0;
}
bool WinUtils::isStatusBarDark() const
{
QSettings settings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)",
QSettings::NativeFormat);
return settings.value("SystemUsesLightTheme", 0).toInt() == 0;
}
bool WinUtils::isLaunchAtStartupEnabled() const
{
return QSettings(R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run)", QSettings::NativeFormat)

View file

@ -33,6 +33,7 @@ public:
static void registerEventFilters();
bool isDarkMode() const override;
bool isStatusBarDark() const override;
bool isLaunchAtStartupEnabled() const override;
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override;
@ -52,6 +53,9 @@ private:
static QPointer<WinUtils> m_instance;
static QScopedPointer<DWMEventFilter> m_eventFilter;
bool m_darkAppThemeActive;
bool m_darkSystemThemeActive;
Q_DISABLE_COPY(WinUtils)
};

View file

@ -52,6 +52,10 @@
#include <QtMath>
#include <qdrawutil.h>
#ifdef Q_OS_MACOS
#include <QOperatingSystemVersion>
#endif
#include <cmath>
#include "core/Resources.h"
@ -288,10 +292,16 @@ namespace Phantom
#ifdef Q_OS_MACOS
QColor tabBarBase(const QPalette& pal)
{
return hack_isLightPalette(pal) ? QRgb(0xD1D1D1) : QRgb(0x252525);
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur) {
return hack_isLightPalette(pal) ? QRgb(0xD4D4D4) : QRgb(0x2A2A2A);
}
return hack_isLightPalette(pal) ? QRgb(0xDD1D1D1) : QRgb(0x252525);
}
QColor tabBarBaseInactive(const QPalette& pal)
{
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur) {
return hack_isLightPalette(pal) ? QRgb(0xF5F5F5) : QRgb(0x2D2D2D);
}
return hack_isLightPalette(pal) ? QRgb(0xF4F4F4) : QRgb(0x282828);
}
#endif
@ -4571,27 +4581,6 @@ QStyle::SubControl BaseStyle::hitTestComplexControl(ComplexControl cc,
return QCommonStyle::hitTestComplexControl(cc, opt, pt, w);
}
QPixmap BaseStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap, const QStyleOption* opt) const
{
// Default icon highlight is way too subtle
if (iconMode == QIcon::Selected) {
QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
QPainter painter(&img);
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
QColor color =
Phantom::DeriveColors::adjustLightness(opt->palette.color(QPalette::Normal, QPalette::Highlight), .25);
color.setAlphaF(0.25);
painter.fillRect(0, 0, img.width(), img.height(), color);
painter.end();
return QPixmap::fromImage(img);
}
return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt);
}
int BaseStyle::styleHint(StyleHint hint,
const QStyleOption* option,
const QWidget* widget,

View file

@ -70,7 +70,6 @@ public:
const QStyleOptionComplex* opt,
SubControl sc,
const QWidget* widget) const override;
QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap, const QStyleOption* opt) const override;
int styleHint(StyleHint hint,
const QStyleOption* option = nullptr,
const QWidget* widget = nullptr,

View file

@ -114,9 +114,9 @@ void DarkStyle::polish(QWidget* widget)
auto palette = widget->palette();
#if defined(Q_OS_MACOS)
if (!osUtils->isDarkMode()) {
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x252525));
palette.setColor(QPalette::Inactive, QPalette::Window, QRgb(0x282828));
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x252525));
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x2A2A2A));
palette.setColor(QPalette::Inactive, QPalette::Window, QRgb(0x2D2D2D));
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x2D2D2D));
}
#elif defined(Q_OS_WIN)
// Register event filter for better dark mode support

View file

@ -115,9 +115,9 @@ void LightStyle::polish(QWidget* widget)
auto palette = widget->palette();
#if defined(Q_OS_MACOS)
if (osUtils->isDarkMode()) {
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0xD1D1D1));
palette.setColor(QPalette::Inactive, QPalette::Window, QRgb(0xF4F4F4));
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0xD1D1D1));
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0xD4D4D4));
palette.setColor(QPalette::Inactive, QPalette::Window, QRgb(0xF5F5F5));
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0xF5F5F5));
}
#elif defined(Q_OS_WIN)
palette.setColor(QPalette::All, QPalette::Window, QRgb(0xFFFFFF));