* Copyright (c) 2023 ~ 2024 KylinSec Co., Ltd.
* kiran-qt5-integration is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*
* Author: tangjie02 <tangjie02@kylinos.com.cn>
*/
#include "plugins/platformtheme/theme.h"
#include <private/qguiapplication_p.h>
#include <private/qiconloader_p.h>
#include <private/qwidgetwindow_p.h>
#include <private/qwindow_p.h>
#include <qpa/qplatformscreen.h>
#define private public
#include <private/qhighdpiscaling_p.h>
#undef private
#include <QApplication>
#include <QGuiApplication>
#include <QIcon>
#include <QLabel>
#include <QLibraryInfo>
#include <QPixmap>
#include <QScreen>
#include <QStyle>
#include <QTimer>
#include <QTranslator>
#include <QVariant>
#include <QWindow>
#include "cmake-macros.h"
#include "gtk-file-dialog.h"
#include "lib/common/configuration.h"
#include "lib/theme/palette.h"
#include "lib/theme/style.h"
#include "plugins/platformtheme/appearance-monitor.h"
#include "plugins/platformtheme/themed-icon-engine.h"
#define THEMED_SVG_ICON_PREFIX "ksvg-"
#define THEMED_RSVG_ICON_PREFIX "krsvg-"
namespace Kiran
{
namespace Platformtheme
{
Theme::Theme(const QStringList& paramList)
: QGenericUnixTheme()
{
init();
}
Theme::~Theme() = default;
bool Theme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const
{
switch (type)
{
case QPlatformTheme::DialogType::FileDialog:
return true;
default:
break;
}
return QGenericUnixTheme::usePlatformNativeDialog(type);
}
QPlatformDialogHelper* Theme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const
{
switch (type)
{
case QPlatformTheme::DialogType::FileDialog:
return new GtkFileDialogHelper();
default:
break;
}
return QGenericUnixTheme::createPlatformDialogHelper(type);
}
QIconEngine* Theme::createIconEngine(const QString& iconName) const
{
if (ThemedIconEngine::isValid(iconName))
{
if (iconName.startsWith(THEMED_SVG_ICON_PREFIX))
{
return new ThemedIconEngine(iconName, ThemedIconEngine::SVG_CONVERT_COMPOSITION);
}
else if (iconName.startsWith(THEMED_RSVG_ICON_PREFIX))
{
return new ThemedIconEngine(iconName, ThemedIconEngine::SVG_CONVERT_INVERT_PIXELS);
}
}
return QGenericUnixTheme::createIconEngine(iconName);
}
QPixmap Theme::standardPixmap(QPlatformTheme::StandardPixmap sp, const QSizeF& size) const
{
return QGenericUnixTheme::standardPixmap(sp, size);
}
QIcon Theme::fileIcon(const QFileInfo& fileInfo, QPlatformTheme::IconOptions iconOptions) const
{
return QGenericUnixTheme::fileIcon(fileInfo, iconOptions);
}
QVariant Theme::themeHint(QPlatformTheme::ThemeHint hint) const
{
switch (hint)
{
case SystemIconThemeName:
return QStringList() << m_settingsMonitor->iconTheme();
case SystemIconFallbackThemeName:
return QStringList() << "hicolor";
case IconThemeSearchPaths:
return QStringList() << ".local/share/icons"
<< "/usr/share/icons"
<< "/usr/local/share/icons";
case StyleNames:
return QStringList{"kiran", "fusion"};
case ShowShortcutsInContextMenus:
return false;
default:
break;
}
return QGenericUnixTheme::themeHint(hint);
}
const QPalette* Theme::palette(QPlatformTheme::Palette type) const
{
if (type != SystemPalette)
{
return QGenericUnixTheme::palette(type);
}
Kiran::Configuration conf;
bool enable = conf.match(qAppName());
if (!enable)
{
return QGenericUnixTheme::palette(type);
}
static QPalette themePalette;
Kiran::Theme::Palette::getDefault()->polish(themePalette);
return &themePalette;
}
void Theme::init()
{
Kiran::Configuration conf;
QTranslator* integrationTranslator = new QTranslator(this);
integrationTranslator->load(QLocale(), "kiran-integration", ".", KQI_INSTALL_TRANSLATIONDIR);
qApp->installTranslator(integrationTranslator);
QTranslator* qtTranslator = new QTranslator(this);
qtTranslator->load(QLocale(), "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));
qApp->installTranslator(qtTranslator);
GtkFileDialogHelper::ensureGtkInitialized();
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
if ( conf.match(qAppName()) )
{
QTimer::singleShot(0, this,[]() {
qDebug() << "kiran-integration disable shortcuts in context menus for" << qAppName();
QGuiApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus,true);
});
}
#endif
qDebug() << "init kiran theme...";
m_settingsMonitor = AppearanceMonitor::instance();
if (!m_settingsMonitor->gtkTheme().contains("dark"))
{
Kiran::Theme::Palette::getDefault()->setBaseColors(Kiran::Theme::g_lightBaseColors);
}
m_scaleFactor = m_settingsMonitor->scaleFactor();
qDebug() << "\tscale factor:" << m_scaleFactor;
updateAppFont(m_settingsMonitor->appFont().family(),
m_settingsMonitor->appFont().pointSize());
qDebug() << "\tapplication font:" << m_settingsMonitor->appFont().family()
<< m_settingsMonitor->appFont().pointSize();
m_titleBarFont.setFamily(m_settingsMonitor->titleBarFont().family());
m_titleBarFont.setPointSize(m_settingsMonitor->titleBarFont().pointSize());
qDebug() << "\ttitle bar font:" << m_settingsMonitor->titleBarFont().family() << m_settingsMonitor->titleBarFont().pointSize();
QObject::connect(m_settingsMonitor, &AppearanceMonitor::appFontChanged, this, &Theme::handleAppFontChanged);
QObject::connect(m_settingsMonitor, &AppearanceMonitor::titleBarFontChanged, this, &Theme::handleTitleBarFontChanged);
QObject::connect(m_settingsMonitor, &AppearanceMonitor::iconThemeChanged, this, &Theme::handleIconThemeChanged);
QObject::connect(m_settingsMonitor, &AppearanceMonitor::scaleFactorChanged, this, &Theme::handleScaleFactorChanged);
QObject::connect(m_settingsMonitor, &AppearanceMonitor::cursorThemeChanged, this, &Theme::handleCursorThemeChanged);
QObject::connect(m_settingsMonitor, &AppearanceMonitor::gtkThemeChanged, this, &Theme::handleThemeChanged);
QObject::connect(qApp, &QGuiApplication::screenAdded, this, &Theme::handleScreenAdded);
handleScaleFactorChanged(m_scaleFactor);
}
void Theme::updateAppFont(const QString& fontFamily, int pointSize)
{
m_systemFont.setFamily(fontFamily);
m_systemFont.setPointSize(pointSize);
auto coreApp = QGuiApplication::instance();
if (!qobject_cast<QApplication*>(coreApp))
{
QGuiApplication::setFont(m_systemFont);
}
else
{
QApplication::setFont(m_systemFont);
}
}
const QFont* Theme::font(QPlatformTheme::Font type) const
{
switch (type)
{
case QPlatformTheme::TitleBarFont:
{
static QFont titleBarFont = QFont(QString());
titleBarFont.setFamily(m_titleBarFont.family());
titleBarFont.setPointSize(m_titleBarFont.pointSize());
return &titleBarFont;
}
case SystemFont:
case FixedFont:
{
static QFont tempFont = QFont(QString());
tempFont.setFamily(m_systemFont.family());
tempFont.setPointSize(m_systemFont.pointSize());
return &tempFont;
}
default:
break;
}
return QGenericUnixTheme::font(type);
}
void Theme::handleAppFontChanged()
{
qDebug() << "application font changed:"
<< m_settingsMonitor->appFont().family()
<< m_settingsMonitor->appFont().pointSize();
updateAppFont(m_settingsMonitor->appFont().family(),
m_settingsMonitor->appFont().pointSize());
}
void Theme::handleTitleBarFontChanged()
{
qDebug() << "title bar font changed:"
<< m_settingsMonitor->titleBarFont().family()
<< m_settingsMonitor->titleBarFont().pointSize();
m_titleBarFont.setFamily(m_settingsMonitor->titleBarFont().family());
m_titleBarFont.setPointSize(m_settingsMonitor->titleBarFont().pointSize());
for (auto window : qGuiApp->allWindows())
{
if (window->inherits("QWidgetWindow"))
{
auto widgetWindow = static_cast<QWidgetWindow*>(window);
if (widgetWindow->widget() && widgetWindow->widget()->inherits("KiranTitlebarWindow"))
{
auto titleBarWidget = widgetWindow->widget();
auto titleLabel = titleBarWidget->findChild<QLabel*>("KiranTitlebarTitle");
titleLabel->setFont(m_titleBarFont);
}
}
}
}
void Theme::handleScaleFactorChanged(int factor)
{
qDebug() << "scale factor changed:"
<< m_settingsMonitor->scaleFactor();
if (enableRealTimeScaling())
{
if (factor == 0)
{
}
else if (factor == 1 || factor == 2)
{
qDebug() << "update scale factor:" << factor;
if (qFuzzyCompare(QHighDpiScaling::m_factor, factor))
{
return;
}
{
for (QScreen* screen : qGuiApp->screens())
{
if (!qFuzzyCompare(QHighDpiScaling::screenSubfactor(screen->handle()), 1))
{
QHighDpiScaling::setScreenFactor(screen, 1);
}
}
}
QHighDpiScaling::setGlobalFactor(factor);
{
for (QScreen* s : qGuiApp->screens())
{
Q_EMIT s->geometryChanged(s->geometry());
}
}
{
for (QWindow* window : QGuiApplication::allWindows())
{
if (window->type() == Qt::ForeignWindow || window->type() == Qt::Desktop)
{
return;
}
if (!window->handle() || !window->isTopLevel())
return;
const QRect currentGeo = QWindowPrivate::get(window)->geometry;
if (!currentGeo.isValid())
return;
QRect nativeGeo = window->handle()->geometry();
qreal scale = QHighDpiScaling::factor(window);
nativeGeo.setSize(currentGeo.size() * scale);
window->handle()->setGeometry(nativeGeo);
QEvent event(QEvent::UpdateRequest);
QGuiApplication::sendEvent(window, &event);
}
}
}
else
{
qDebug() << "not support this factor:" << factor;
return;
}
}
else
{
qDebug() << "disable real time scaling,ignore scale factor changed!";
}
}
void Theme::handleCursorThemeChanged()
{
QGuiApplication::setOverrideCursor(QCursor());
QGuiApplication::restoreOverrideCursor();
}
bool Theme::enableRealTimeScaling()
{
static bool enable = !qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO") &&
!qEnvironmentVariableIsSet("QT_SCALE_FACTOR") &&
!qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") &&
!qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS") &&
!QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling) &&
!QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling) &&
qGuiApp->platformName().endsWith("xcb");
return enable;
}
void Theme::handleScreenAdded(QScreen* screen)
{
if (m_scaleFactor == 0)
{
}
else if (m_scaleFactor == 1 || m_scaleFactor == 2)
{
}
}
void Theme::handleIconThemeChanged()
{
qInfo() << "update icon theme:" << m_settingsMonitor->iconTheme();
QIconLoader::instance()->updateSystemTheme();
QEvent update(QEvent::UpdateRequest);
for (QWindow* window : qGuiApp->allWindows())
{
if (window->type() == Qt::Desktop)
continue;
qApp->sendEvent(window, &update);
}
}
void Theme::handleThemeChanged()
{
auto themeName = m_settingsMonitor->gtkTheme();
if (themeName.compare("kiran") == 0)
{
Kiran::Theme::Palette::getDefault()->setBaseColors(Kiran::Theme::g_lightBaseColors);
}
else
{
Kiran::Theme::Palette::getDefault()->setBaseColors(Kiran::Theme::g_darkBaseColors);
}
QTimer::singleShot(0, [this]
{
QWindowSystemInterfacePrivate::ThemeChangeEvent event(nullptr);
QPalette* oldPalette = nullptr;
oldPalette = QGuiApplicationPrivate::app_pal;
QGuiApplicationPrivate::app_pal = new QPalette(*palette(SystemPalette));
delete oldPalette;
QGuiApplicationPrivate::processThemeChanged(&event);
emit qApp->paletteChanged(*palette(SystemPalette));
});
}
}
}