view src/gui/theme.cc @ 393:963047512d34

*: clang-format
author Paper <paper@tflc.us>
date Fri, 07 Nov 2025 07:16:15 -0500
parents 47c9f8502269
children
line wrap: on
line source

#include "gui/theme.h"
#include "core/config.h"
#include "core/session.h"
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QStyle>
#include <QStyleFactory>
#include <QTextStream>
#include <QtGlobal>
#ifdef MACOSX
# include "sys/osx/dark_theme.h"
#elif defined(WIN32)
# include "sys/win32/dark_theme.h"
#else
# ifdef GLIB
#  include "sys/glib/dark_theme.h"
# endif
# ifdef HAVE_XCB
#  include "sys/x11/dark_theme.h"
# endif
#endif

/* Weird quirks of this implementation:
 *   1. Dark mode stuff in Qt changes a lot and Qt 5 and Qt 6 are massively different
 *   2. Some widgets, i.e. QTabWidget, QTabBar, etc., just completely IGNORE the QPalette setting
 *      on different platforms and the only way to fix it is by using Fusion
 *   3. Windows dark mode support in Qt 6.5 (with Fusion) is completely unavoidable
 */

namespace Theme {

ThemeManager::ThemeManager(Theme theme)
{
	this->theme = theme;
}

Theme ThemeManager::GetTheme() const
{
	return theme;
}

bool ThemeManager::IsInDarkTheme() const
{
	switch (theme) {
	case Theme::Default:
#ifdef MACOSX
		if (osx::DarkThemeAvailable())
			return osx::IsInDarkTheme();
#elif defined(WIN32)
		if (win32::DarkThemeAvailable())
			return win32::IsInDarkTheme();
#else
# ifdef HAVE_XCB
		if (x11::IsInDarkTheme())
			return true;
# endif
# ifdef GLIB
		if (glib::IsInDarkTheme())
			return true;
# endif
		break;
#endif
	default:
		break;
	}
	return (theme == Theme::Dark);
}

void ThemeManager::SetToDarkTheme()
{
	/* macOS >= 10.14 has its own global dark theme,
	   use it :) */
#ifdef MACOSX
	if (osx::DarkThemeAvailable())
		osx::SetToDarkTheme();
	else
#elif defined(WIN32)
	if (win32::DarkThemeAvailable())
		win32::SetTitleBarsToBlack(true);
#endif
		SetStyleSheet(Theme::Dark);
}

void ThemeManager::SetToLightTheme()
{
#ifdef MACOSX
	if (osx::DarkThemeAvailable())
		osx::SetToLightTheme();
	else
#elif defined(WIN32)
	if (win32::DarkThemeAvailable())
		win32::SetTitleBarsToBlack(false);
#endif
		SetStyleSheet(Theme::Light);
}

Theme ThemeManager::GetCurrentOSTheme() const
{
	return IsInDarkTheme() ? Theme::Dark : Theme::Light;
}

/* this function is private, and should stay that way */
void ThemeManager::SetStyleSheet(Theme theme)
{
	switch (theme) {
	case Theme::Dark: {
		const QColor darkGray(53, 53, 53);
		const QColor gray(128, 128, 128);
		const QColor black(25, 25, 25);
		const QColor blue(42, 130, 218);

		QPalette pal;

		pal.setColor(QPalette::Window, darkGray);
		pal.setColor(QPalette::WindowText, Qt::white);
		pal.setColor(QPalette::Base, black);
		pal.setColor(QPalette::AlternateBase, darkGray);
		pal.setColor(QPalette::ToolTipBase, blue);
		pal.setColor(QPalette::ToolTipText, Qt::white);
		pal.setColor(QPalette::Text, Qt::white);
		pal.setColor(QPalette::Button, darkGray);
		pal.setColor(QPalette::ButtonText, Qt::white);
		pal.setColor(QPalette::Link, blue);
		pal.setColor(QPalette::Highlight, blue);
		pal.setColor(QPalette::HighlightedText, Qt::black);

		pal.setColor(QPalette::Active, QPalette::Button, gray.darker());
		pal.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
		pal.setColor(QPalette::Disabled, QPalette::WindowText, gray);
		pal.setColor(QPalette::Disabled, QPalette::Text, gray);
		pal.setColor(QPalette::Disabled, QPalette::Light, darkGray);

		qApp->setPalette(pal);

#ifdef WIN32
		qApp->setStyleSheet([] {
			QFile f(":/dark.qss");
			if (!f.exists())
				return QStringLiteral("");
			f.open(QFile::ReadOnly | QFile::Text);
			QTextStream ts(&f);
			return ts.readAll();
		}());
#endif
		break;
	}
	default:
		/* this sucks */
		QPalette pal(QApplication::style()->standardPalette());
#ifdef WIN32 /* fuck you Qt 6 */
		pal.setColor(QPalette::Window, QColor(0xF0, 0xF0, 0xF0));
#endif
		qApp->setPalette(pal);

		qApp->setStyleSheet("");
		break;
	}
}

void ThemeManager::SetTheme(Theme theme)
{
	switch (theme) {
	case Theme::Light:
		SetToLightTheme();
		break;
	case Theme::Dark:
		SetToDarkTheme();
		break;
	case Theme::Default:
		if (GetCurrentOSTheme() == Theme::Light)
			SetToLightTheme();
		else
			SetToDarkTheme();
		break;
	}
	this->theme = theme;
}

void ThemeManager::RepaintCurrentTheme()
{
	SetTheme(theme);
}

} // namespace Theme