6#if defined(QT_UTILITIES_GUI_QTWIDGETS)
16#include "resources/config.h"
18#if defined(QT_UTILITIES_GUI_QTWIDGETS)
19#include "ui_qtappearanceoptionpage.h"
20#include "ui_qtenvoptionpage.h"
21#include "ui_qtlanguageoptionpage.h"
24#include <c++utilities/application/commandlineutils.h>
28#include <QGuiApplication>
31#include <QOperatingSystemVersion>
34#include <QStringBuilder>
35#include <QVersionNumber>
37#if defined(QT_UTILITIES_GUI_QTWIDGETS)
40#include <QStyleFactory>
43#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
44#include <QOperatingSystemVersion>
45#define QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
61#if defined(QT_UTILITIES_GUI_QTWIDGETS)
63 QString initialWidgetStyle;
64 QString styleSheetPath;
77#if defined(QT_UTILITIES_GUI_QTWIDGETS)
78 bool customWidgetStyle;
79 bool customStyleSheet;
84#if defined(QT_UTILITIES_GUI_QTWIDGETS)
96#if defined(QT_UTILITIES_GUI_QTWIDGETS)
97 , customWidgetStyle(false)
98 , customStyleSheet(false)
103#if defined(QT_UTILITIES_GUI_QTWIDGETS)
105 , retranslatable(false)
131#if defined(QT_UTILITIES_GUI_QTWIDGETS)
143void QtSettings::disableNotices()
145 m_d->showNotices =
false;
156void QtSettings::setRetranslatable(
bool retranslatable)
158 m_d->retranslatable = retranslatable;
167 return m_d->customFont;
178 settings.beginGroup(QStringLiteral(
"qt"));
179 m_d->font.fromString(settings.value(QStringLiteral(
"font")).toString());
180 m_d->customFont = settings.value(QStringLiteral(
"customfont"),
false).toBool();
181 m_d->palette = settings.value(QStringLiteral(
"palette")).value<QPalette>();
182 m_d->customPalette = settings.value(QStringLiteral(
"custompalette"),
false).toBool();
183#if defined(QT_UTILITIES_GUI_QTWIDGETS)
184 m_d->widgetStyle = settings.value(QStringLiteral(
"widgetstyle"), m_d->widgetStyle).toString();
185 m_d->customWidgetStyle = settings.value(QStringLiteral(
"customwidgetstyle"),
false).toBool();
186 m_d->styleSheetPath = settings.value(QStringLiteral(
"stylesheetpath"), m_d->styleSheetPath).toString();
187 m_d->customStyleSheet = settings.value(QStringLiteral(
"customstylesheet"),
false).toBool();
189 m_d->iconTheme = settings.value(QStringLiteral(
"icontheme"), m_d->iconTheme).toString();
190 m_d->customIconTheme = settings.value(QStringLiteral(
"customicontheme"),
false).toBool();
191 m_d->localeName = settings.value(QStringLiteral(
"locale"), m_d->localeName).toString();
192 m_d->customLocale = settings.value(QStringLiteral(
"customlocale"),
false).toBool();
193 m_d->additionalPluginDirectory = settings.value(QStringLiteral(
"plugindir")).toString();
194 m_d->additionalIconThemeSearchPath = settings.value(QStringLiteral(
"iconthemepath")).toString();
197 if (qEnvironmentVariableIntValue(
"QT_DEBUG_SETTINGS")) {
198 std::cerr <<
"Restored Qt settings values\n";
207 settings.beginGroup(QStringLiteral(
"qt"));
208 settings.setValue(QStringLiteral(
"font"), QVariant(m_d->font.toString()));
209 settings.setValue(QStringLiteral(
"customfont"), m_d->customFont);
210 settings.setValue(QStringLiteral(
"palette"), QVariant(m_d->palette));
211 settings.setValue(QStringLiteral(
"custompalette"), m_d->customPalette);
212#if defined(QT_UTILITIES_GUI_QTWIDGETS)
213 settings.setValue(QStringLiteral(
"widgetstyle"), m_d->widgetStyle);
214 settings.setValue(QStringLiteral(
"customwidgetstyle"), m_d->customWidgetStyle);
215 settings.setValue(QStringLiteral(
"stylesheetpath"), m_d->styleSheetPath);
216 settings.setValue(QStringLiteral(
"customstylesheet"), m_d->customStyleSheet);
218 settings.setValue(QStringLiteral(
"icontheme"), m_d->iconTheme);
219 settings.setValue(QStringLiteral(
"customicontheme"), m_d->customIconTheme);
220 settings.setValue(QStringLiteral(
"locale"), m_d->localeName);
221 settings.setValue(QStringLiteral(
"customlocale"), m_d->customLocale);
222 settings.setValue(QStringLiteral(
"plugindir"), m_d->additionalPluginDirectory);
223 settings.setValue(QStringLiteral(
"iconthemepath"), m_d->additionalIconThemeSearchPath);
226 if (qEnvironmentVariableIntValue(
"QT_DEBUG_SETTINGS")) {
227 std::cerr <<
"Set Qt settings values\n";
236static QMap<QString, QString> scanIconThemes(
const QStringList &searchPaths)
238 auto res = QMap<QString, QString>();
239 for (
const auto &searchPath : searchPaths) {
240 const auto dir = QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
241 for (
const auto &iconTheme : dir) {
242 auto indexFile = QFile(searchPath % QChar(
'/') % iconTheme % QStringLiteral(
"/index.theme"));
243 auto index = QByteArray();
244 if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) {
245 const auto iconThemeSection = index.indexOf(
"[Icon Theme]");
246 const auto nameStart = index.indexOf(
"Name=", iconThemeSection != -1 ? iconThemeSection : 0);
247 if (nameStart != -1) {
248 auto nameLength = index.indexOf(
"\n", nameStart) - nameStart - 5;
249 if (nameLength > 0) {
250 auto displayName = QString::fromUtf8(index.mid(nameStart + 5, nameLength));
251 if (displayName != iconTheme) {
252 displayName += QChar(
' ') % QChar(
'(') % iconTheme % QChar(
')');
254 res[displayName] = iconTheme;
259 res[iconTheme] = iconTheme;
279 if (m_d->additionalPluginDirectory != m_d->previousPluginDirectory) {
280 if (!m_d->previousPluginDirectory.isEmpty()) {
281 QCoreApplication::removeLibraryPath(m_d->previousPluginDirectory);
283 if (!m_d->additionalPluginDirectory.isEmpty()) {
284 QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory);
286 m_d->previousPluginDirectory = m_d->additionalPluginDirectory;
288 if (m_d->additionalIconThemeSearchPath != m_d->previousIconThemeSearchPath) {
289 auto paths = QIcon::themeSearchPaths();
290 if (!m_d->previousIconThemeSearchPath.isEmpty()) {
291 paths.removeAll(m_d->previousIconThemeSearchPath);
293 if (!m_d->additionalIconThemeSearchPath.isEmpty()) {
294 paths.append(m_d->additionalIconThemeSearchPath);
296 m_d->previousIconThemeSearchPath = m_d->additionalIconThemeSearchPath;
297 QIcon::setThemeSearchPaths(paths);
300#if defined(QT_UTILITIES_GUI_QTWIDGETS)
302 auto styleSheet = QString();
303 if (m_d->customStyleSheet && !m_d->styleSheetPath.isEmpty()) {
304 auto file = QFile(m_d->styleSheetPath);
305 if (!file.open(QFile::ReadOnly)) {
306 std::cerr <<
"Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
308 styleSheet.append(file.readAll());
309 if (file.error() != QFile::NoError) {
310 std::cerr <<
"Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
316 if (m_d->customFont) {
317 if (!m_d->initialFont.has_value()) {
318 m_d->initialFont = QGuiApplication::font();
320 QGuiApplication::setFont(m_d->font);
321 }
else if (m_d->initialFont.has_value()) {
322 QGuiApplication::setFont(m_d->initialFont.value());
324#if defined(QT_UTILITIES_GUI_QTWIDGETS)
325#ifdef QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
326 if (m_d->initialWidgetStyle.isEmpty()) {
329 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) {
330 m_d->initialWidgetStyle = QStringLiteral(
"Fusion");
334 if (m_d->customWidgetStyle) {
335#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
336 const auto *
const currentStyle = QApplication::style();
337 if (m_d->initialWidgetStyle.isEmpty() && currentStyle) {
338 m_d->initialWidgetStyle = currentStyle->name();
341 QApplication::setStyle(m_d->widgetStyle);
342 }
else if (!m_d->initialWidgetStyle.isEmpty()) {
343 QApplication::setStyle(m_d->initialWidgetStyle);
345 if (
auto *
const qapp = qobject_cast<QApplication *>(QApplication::instance())) {
346 qapp->setStyleSheet(styleSheet);
348 std::cerr <<
"Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data()
349 <<
"\" because no QApplication has been instantiated." << std::endl;
352 if (m_d->customPalette) {
353 QGuiApplication::setPalette(m_d->palette);
355 QGuiApplication::setPalette(QPalette());
358 if (m_d->customIconTheme) {
359 QIcon::setThemeName(m_d->iconTheme);
360 }
else if (!m_d->initialIconTheme.isEmpty()) {
361 if (m_d->iconTheme != m_d->initialIconTheme) {
363 QIcon::setThemeName(m_d->initialIconTheme);
372 const auto bundledIconThemes = scanIconThemes(QStringList(QStringLiteral(
":/icons")));
373 if (m_d->isPaletteDark && bundledIconThemes.contains(QStringLiteral(
"default-dark"))) {
374 QIcon::setThemeName(QStringLiteral(
"default-dark"));
375 }
else if (bundledIconThemes.contains(QStringLiteral(
"default"))) {
376 QIcon::setThemeName(QStringLiteral(
"default"));
381 m_d->previousLocale = QLocale();
382 QLocale::setDefault(m_d->customLocale ? QLocale(m_d->localeName) : m_d->defaultLocale);
385 static auto debugInfoLogged =
false;
386 if (debugInfoLogged) {
389 const auto debugLoggingEnabled = CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER
"_LOG_QT_CONFIG");
390 if (debugLoggingEnabled.has_value() && debugLoggingEnabled.value()) {
391 if (
const auto os = QOperatingSystemVersion::current(); os.type() !=
static_cast<decltype(os.type())
>(QOperatingSystemVersion::Unknown)) {
392 const auto version = QVersionNumber(os.majorVersion(), os.minorVersion(), os.microVersion());
393 std::cerr <<
"OS name and version: " << os.name().toStdString() <<
' ' << version.toString().toStdString() <<
'\n';
395 std::cerr <<
"Qt version: " << qVersion() <<
'\n';
396 std::cerr <<
"Qt platform (set QT_QPA_PLATFORM to override): " << QGuiApplication::platformName().toStdString() <<
'\n';
397 std::cerr <<
"Qt locale: " << QLocale().name().toStdString() <<
'\n';
398 std::cerr <<
"Qt library paths: " << QCoreApplication::libraryPaths().join(
':').toStdString() <<
'\n';
399 std::cerr <<
"Qt theme search paths: " << QIcon::themeSearchPaths().join(
':').toStdString() <<
'\n';
401 std::cerr <<
"Is Qt palette dark: " << (m_d->isPaletteDark ?
"yes" :
"no") <<
'\n';
402 debugInfoLogged =
true;
428 if (
auto iconTheme = QIcon::themeName(); iconTheme == QStringLiteral(
"default") || iconTheme == QStringLiteral(
"default-dark")) {
429 QIcon::setThemeName(m_d->isPaletteDark ? QStringLiteral(
"default-dark") : QStringLiteral(
"default"));
456 return m_d->isPaletteDark;
464 return m_d->previousLocale != QLocale();
467#if defined(QT_UTILITIES_GUI_QTWIDGETS)
480 category->
setIcon(QIcon::fromTheme(QStringLiteral(
"qtcreator"), QIcon(QStringLiteral(
":/qtutilities/icons/hicolor/48x48/apps/qtcreator.svg"))));
481 category->
assignPages({
new QtAppearanceOptionPage(*m_d),
new QtLanguageOptionPage(*m_d),
new QtEnvOptionPage(*m_d) });
485QtAppearanceOptionPage::QtAppearanceOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
486 : QtAppearanceOptionPageBase(parentWidget)
487 , m_settings(settings)
488 , m_fontDialog(nullptr)
492QtAppearanceOptionPage::~QtAppearanceOptionPage()
496bool QtAppearanceOptionPage::apply()
498 m_settings.font = ui()->fontComboBox->currentFont();
499 m_settings.customFont = !ui()->fontCheckBox->isChecked();
500 m_settings.widgetStyle = ui()->widgetStyleComboBox->currentText();
501 m_settings.customWidgetStyle = !ui()->widgetStyleCheckBox->isChecked();
502 m_settings.styleSheetPath = ui()->styleSheetPathSelection->lineEdit()->text();
503 m_settings.customStyleSheet = !ui()->styleSheetCheckBox->isChecked();
504 m_settings.palette = m_settings.selectedPalette;
505 m_settings.customPalette = !ui()->paletteCheckBox->isChecked();
507 = ui()->iconThemeComboBox->currentIndex() != -1 ? ui()->iconThemeComboBox->currentData().toString() : ui()->iconThemeComboBox->currentText();
508 m_settings.customIconTheme = !ui()->iconThemeCheckBox->isChecked();
512void QtAppearanceOptionPage::reset()
514 ui()->fontComboBox->setCurrentFont(m_settings.font);
515 ui()->fontCheckBox->setChecked(!m_settings.customFont);
516 ui()->widgetStyleComboBox->setCurrentText(
517 m_settings.widgetStyle.isEmpty() ? (QApplication::style() ? QApplication::style()->objectName() : QString()) : m_settings.widgetStyle);
518 ui()->widgetStyleCheckBox->setChecked(!m_settings.customWidgetStyle);
519 ui()->styleSheetPathSelection->lineEdit()->setText(m_settings.styleSheetPath);
520 ui()->styleSheetCheckBox->setChecked(!m_settings.customStyleSheet);
521 m_settings.selectedPalette = m_settings.palette;
522 ui()->paletteCheckBox->setChecked(!m_settings.customPalette);
523 int iconThemeIndex = ui()->iconThemeComboBox->findData(m_settings.iconTheme);
524 if (iconThemeIndex != -1) {
525 ui()->iconThemeComboBox->setCurrentIndex(iconThemeIndex);
527 ui()->iconThemeComboBox->setCurrentText(m_settings.iconTheme);
529 ui()->iconThemeCheckBox->setChecked(!m_settings.customIconTheme);
532QWidget *QtAppearanceOptionPage::setupWidget()
535 auto *widget = QtAppearanceOptionPageBase::setupWidget();
536 if (!m_settings.showNotices) {
541 ui()->widgetStyleComboBox->addItems(QStyleFactory::keys());
544 ui()->styleSheetPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
547 QObject::connect(ui()->fontPushButton, &QPushButton::clicked, widget, [
this] {
549 m_fontDialog =
new QFontDialog(this->widget());
550 m_fontDialog->setCurrentFont(ui()->fontComboBox->font());
551 QObject::connect(m_fontDialog, &QFontDialog::fontSelected, ui()->fontComboBox, &QFontComboBox::setCurrentFont);
552 QObject::connect(ui()->fontComboBox, &QFontComboBox::currentFontChanged, m_fontDialog, &QFontDialog::setCurrentFont);
554 m_fontDialog->show();
558 QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, ui()->paletteToolButton,
562 const auto iconThemes = scanIconThemes(QIcon::themeSearchPaths() << QStringLiteral(
"/usr/share/icons/"));
563 auto *iconThemeComboBox = ui()->iconThemeComboBox;
564 for (
auto i = iconThemes.begin(), end = iconThemes.end(); i != end; ++i) {
565 const auto &displayName = i.key();
566 const auto &
id = i.value();
567 if (
const auto existingItemIndex = iconThemeComboBox->findData(
id); existingItemIndex != -1) {
568 iconThemeComboBox->setItemText(existingItemIndex, displayName);
570 iconThemeComboBox->addItem(displayName,
id);
577QtLanguageOptionPage::QtLanguageOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
578 : QtLanguageOptionPageBase(parentWidget)
579 , m_settings(settings)
583QtLanguageOptionPage::~QtLanguageOptionPage()
587bool QtLanguageOptionPage::apply()
589 m_settings.localeName = ui()->localeComboBox->currentText();
590 m_settings.customLocale = !ui()->localeCheckBox->isChecked();
594void QtLanguageOptionPage::reset()
596 ui()->localeComboBox->setCurrentText(m_settings.localeName);
597 ui()->localeCheckBox->setChecked(!m_settings.customLocale);
600QWidget *QtLanguageOptionPage::setupWidget()
603 auto *widget = QtLanguageOptionPageBase::setupWidget();
604 if (m_settings.retranslatable) {
609 auto *localeComboBox = ui()->localeComboBox;
610 const auto locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
611 for (
const QLocale &locale : locales) {
612 localeComboBox->addItem(locale.name());
615 auto *languageLabel = ui()->languageLabel;
616 QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, languageLabel, [languageLabel, localeComboBox] {
617 const auto selectedLocale = QLocale(localeComboBox->currentText());
618 const auto currentLocale = QLocale();
619 const auto territory =
620#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
621 currentLocale.territoryToString(selectedLocale.territory());
623 currentLocale.countryToString(selectedLocale.country());
625 languageLabel->setText(QCoreApplication::translate(
"QtGui::QtLanguageOptionPage",
"recognized by Qt as") % QStringLiteral(
" <i>")
626 % currentLocale.languageToString(selectedLocale.language()) % QChar(
',') % QChar(
' ') % territory % QStringLiteral(
"</i>"));
631QtEnvOptionPage::QtEnvOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
632 : QtEnvOptionPageBase(parentWidget)
633 , m_settings(settings)
637QtEnvOptionPage::~QtEnvOptionPage()
641bool QtEnvOptionPage::apply()
643 m_settings.additionalPluginDirectory = ui()->pluginPathSelection->lineEdit()->text();
644 m_settings.additionalIconThemeSearchPath = ui()->iconThemeSearchPathSelection->lineEdit()->text();
649void QtEnvOptionPage::reset()
651 ui()->pluginPathSelection->lineEdit()->setText(m_settings.additionalPluginDirectory);
652 ui()->iconThemeSearchPathSelection->lineEdit()->setText(m_settings.additionalIconThemeSearchPath);
656QWidget *QtEnvOptionPage::setupWidget()
659 return QtEnvOptionPageBase::setupWidget();
676#if defined(QT_UTILITIES_GUI_QTWIDGETS)
The OptionCategory class wraps associated option pages.
void setDisplayName(const QString &displayName)
Sets the display name of the category.
void assignPages(const QList< OptionPage * > &pages)
Assigns the specified pages to the category.
void setIcon(const QIcon &icon)
Sets the icon of the category.
static QPalette getPalette(QWidget *parent, const QPalette &init=QPalette(), const QPalette &parentPal=QPalette(), int *result=nullptr)
void save(QSettings &settings) const
Saves the settings to the specified QSettings object.
OptionCategory * category()
QtSettings()
Creates a new settings object.
void reapplyDefaultIconTheme(bool isPaletteDark)
Re-applies default icon theme assuming the palette is dark or not depending on isPaletteDark.
~QtSettings()
Destroys the settings object.
void restore(QSettings &settings)
Restores the settings from the specified QSettings object.
bool hasLocaleChanged() const
Returns whether the last apply() call has changed the default locale.
bool isPaletteDark()
Returns whether the palette is dark.
bool hasCustomFont() const
Returns whether a custom font is set.
void apply()
Applies the current configuration.
void reevaluatePaletteAndDefaultIconTheme()
Re-evaluates whether the palette is dark and re-applies default icon theme.
QT_UTILITIES_EXPORT QString & additionalTranslationFilePath()
Allows to set an additional search path for translation files.
QT_UTILITIES_EXPORT std::optional< bool > isDarkModeEnabled()
Returns whether dark mode is enabled.
QT_UTILITIES_EXPORT bool isPaletteDark(const QPalette &palette=QPalette())
Returns whether palette is dark.
#define INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(SomeClass)
Instantiates a class declared with BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE in a convenient way.
QString previousPluginDirectory
QString previousIconThemeSearchPath
QString additionalPluginDirectory
std::optional< QFont > initialFont
QString additionalIconThemeSearchPath