Skip to content

Commit a654ce6

Browse files
committed
Qt: Style QMenu and QToolButton with stylesheet on MacOS
Using a QStyleProxy would be preferable, but this is fast enough and stops the native theme from looking so ugly.
1 parent e0e245d commit a654ce6

File tree

3 files changed

+122
-7
lines changed

3 files changed

+122
-7
lines changed

src/duckstation-qt/mainwindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2891,7 +2891,7 @@ void MainWindow::changeEvent(QEvent* event)
28912891

28922892
if (event->type() == QEvent::StyleChange)
28932893
{
2894-
QtHost::SetIconThemeFromStyle();
2894+
QtHost::UpdateThemeOnStyleChange();
28952895
emit themeChanged(QtHost::IsDarkApplicationTheme());
28962896
}
28972897

src/duckstation-qt/qthost.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ bool IsDarkApplicationTheme();
352352
bool IsStyleSheetApplicationTheme();
353353

354354
/// Sets the icon theme, based on the current style (light/dark).
355-
void SetIconThemeFromStyle();
355+
void UpdateThemeOnStyleChange();
356356

357357
/// Sets batch mode (exit after game shutdown).
358358
bool InBatchMode();

src/duckstation-qt/qtthemes.cpp

Lines changed: 120 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
namespace QtHost {
1818
static void SetThemeAttributes(bool is_stylesheet_theme, bool is_variable_color_theme, bool is_dark_theme);
1919
static void SetStyleFromSettings();
20+
static QString GetNativeThemeStylesheet();
21+
static bool NativeThemeStylesheetNeedsUpdate();
2022

2123
namespace {
2224
struct State
2325
{
24-
std::string current_theme_name;
2526
QString unthemed_style_name;
2627
QPalette unthemed_palette;
2728
bool is_stylesheet_theme = false;
@@ -50,7 +51,7 @@ void QtHost::UpdateApplicationTheme()
5051
}
5152

5253
SetStyleFromSettings();
53-
SetIconThemeFromStyle();
54+
UpdateThemeOnStyleChange();
5455
}
5556

5657
void QtHost::SetThemeAttributes(bool is_stylesheet_theme, bool is_variable_color_theme, bool is_dark_theme)
@@ -502,7 +503,7 @@ QToolBar {
502503
SetThemeAttributes(false, true, false);
503504
qApp->setStyle(s_state.unthemed_style_name);
504505
qApp->setPalette(s_state.unthemed_palette);
505-
qApp->setStyleSheet(QString());
506+
qApp->setStyleSheet(GetNativeThemeStylesheet());
506507
}
507508
}
508509

@@ -524,9 +525,14 @@ bool QtHost::IsStyleSheetApplicationTheme()
524525
return s_state.is_stylesheet_theme;
525526
}
526527

527-
void QtHost::SetIconThemeFromStyle()
528+
void QtHost::UpdateThemeOnStyleChange()
528529
{
529-
QIcon::setThemeName(IsDarkApplicationTheme() ? QStringLiteral("white") : QStringLiteral("black"));
530+
const QString new_theme_name = IsDarkApplicationTheme() ? QStringLiteral("white") : QStringLiteral("black");
531+
if (QIcon::themeName() != new_theme_name)
532+
QIcon::setThemeName(new_theme_name);
533+
534+
if (NativeThemeStylesheetNeedsUpdate())
535+
qApp->setStyleSheet(GetNativeThemeStylesheet());
530536
}
531537

532538
const char* Host::GetDefaultFullscreenUITheme()
@@ -552,3 +558,112 @@ const char* Host::GetDefaultFullscreenUITheme()
552558
else
553559
return IsDarkApplicationTheme() ? "Dark" : "Light";
554560
}
561+
562+
bool QtHost::NativeThemeStylesheetNeedsUpdate()
563+
{
564+
#ifdef __APPLE__
565+
// See below, only used on MacOS.
566+
return s_state.is_variable_color_theme;
567+
#else
568+
return false;
569+
#endif
570+
}
571+
572+
QString QtHost::GetNativeThemeStylesheet()
573+
{
574+
QString ret;
575+
#ifdef __APPLE__
576+
// Qt's native style on MacOS is... not great.
577+
// We re-theme the tool buttons to look like Cocoa tool buttons, and fix up popup menus.
578+
ret = QStringLiteral(R"(
579+
QMenu {
580+
border-radius: 10px;
581+
padding: 4px 0;
582+
}
583+
QMenu::item {
584+
padding: 4px 15px;
585+
border-radius: 8px;
586+
margin: 0 2px;
587+
}
588+
QMenu::icon,
589+
QMenu::indicator {
590+
left: 8px;
591+
}
592+
QMenu::icon:checked {
593+
border-radius: 4px;
594+
}
595+
QMenu::separator {
596+
height: 1px;
597+
margin: 4px 8px;
598+
}
599+
QToolButton {
600+
border: none;
601+
background: transparent;
602+
padding: 5px;
603+
border-radius: 10px;
604+
})");
605+
if (IsDarkApplicationTheme())
606+
{
607+
ret += QStringLiteral(R"(
608+
QMenu {
609+
background-color: #161616;
610+
border: 1px solid #2c2c2c;
611+
}
612+
QMenu::item {
613+
color: #dcdcdc;
614+
}
615+
QMenu::item:selected {
616+
background-color: #2b4ab3;
617+
color: #ffffff;
618+
}
619+
QMenu::icon:checked {
620+
background: #414141;
621+
border: 1px solid #777;
622+
}
623+
QMenu::separator {
624+
background: #3b3b3b;
625+
}
626+
QToolButton:checked {
627+
background-color: #454645;
628+
}
629+
QToolButton:hover {
630+
background-color: #393c3c;
631+
}
632+
QToolButton:pressed {
633+
background-color: #808180;
634+
})");
635+
}
636+
else
637+
{
638+
ret += QStringLiteral(R"(
639+
QMenu {
640+
background-color: #bdbdbd;
641+
border: 1px solid #d5d5d4;
642+
}
643+
QMenu::item {
644+
color: #1d1d1d;
645+
}
646+
QMenu::item:selected {
647+
background-color: #2e5dc9;
648+
color: #ffffff;
649+
}
650+
QMenu::icon:checked {
651+
background: #414141;
652+
border: 1px solid #777;
653+
}
654+
QMenu::separator {
655+
background: #a9a9a9;
656+
}
657+
QToolButton:checked {
658+
background-color: #e2e2e2;
659+
}
660+
QToolButton:hover {
661+
background-color: #f0f0f0;
662+
}
663+
QToolButton:pressed {
664+
background-color: #8c8c8c;
665+
})");
666+
}
667+
#endif
668+
return ret;
669+
}

0 commit comments

Comments
 (0)