15#include "resources/config.h"
17#include "ui_qtappearanceoptionpage.h"
18#include "ui_qtenvoptionpage.h"
19#include "ui_qtlanguageoptionpage.h"
21#include <c++utilities/application/commandlineutils.h>
27#include <QOperatingSystemVersion>
29#include <QStringBuilder>
30#include <QStyleFactory>
31#include <QVersionNumber>
33#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
34#include <QOperatingSystemVersion>
35#define QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
75 : iconTheme(QIcon::themeName())
76 , initialIconTheme(iconTheme)
77 , localeName(defaultLocale.name())
79 , customPalette(false)
80 , customWidgetStyle(false)
81 , customStyleSheet(false)
82 , customIconTheme(false)
86 , retranslatable(false)
97QtSettings::QtSettings()
107QtSettings::~QtSettings()
122void QtSettings::disableNotices()
124 m_d->showNotices =
false;
135void QtSettings::setRetranslatable(
bool retranslatable)
137 m_d->retranslatable = retranslatable;
143bool QtSettings::hasCustomFont()
const
145 return m_d->customFont;
154void QtSettings::restore(QSettings &settings)
156 settings.beginGroup(QStringLiteral(
"qt"));
157 m_d->font.fromString(settings.value(QStringLiteral(
"font")).toString());
158 m_d->customFont = settings.value(QStringLiteral(
"customfont"),
false).toBool();
159 m_d->palette = settings.value(QStringLiteral(
"palette")).value<QPalette>();
160 m_d->customPalette = settings.value(QStringLiteral(
"custompalette"),
false).toBool();
161 m_d->widgetStyle = settings.value(QStringLiteral(
"widgetstyle"), m_d->widgetStyle).toString();
162 m_d->customWidgetStyle = settings.value(QStringLiteral(
"customwidgetstyle"),
false).toBool();
163 m_d->styleSheetPath = settings.value(QStringLiteral(
"stylesheetpath"), m_d->styleSheetPath).toString();
164 m_d->customStyleSheet = settings.value(QStringLiteral(
"customstylesheet"),
false).toBool();
165 m_d->iconTheme = settings.value(QStringLiteral(
"icontheme"), m_d->iconTheme).toString();
166 m_d->customIconTheme = settings.value(QStringLiteral(
"customicontheme"),
false).toBool();
167 m_d->localeName = settings.value(QStringLiteral(
"locale"), m_d->localeName).toString();
168 m_d->customLocale = settings.value(QStringLiteral(
"customlocale"),
false).toBool();
169 m_d->additionalPluginDirectory = settings.value(QStringLiteral(
"plugindir")).toString();
170 m_d->additionalIconThemeSearchPath = settings.value(QStringLiteral(
"iconthemepath")).toString();
178void QtSettings::save(QSettings &settings)
const
180 settings.beginGroup(QStringLiteral(
"qt"));
181 settings.setValue(QStringLiteral(
"font"), QVariant(m_d->font.toString()));
182 settings.setValue(QStringLiteral(
"customfont"), m_d->customFont);
183 settings.setValue(QStringLiteral(
"palette"), QVariant(m_d->palette));
184 settings.setValue(QStringLiteral(
"custompalette"), m_d->customPalette);
185 settings.setValue(QStringLiteral(
"widgetstyle"), m_d->widgetStyle);
186 settings.setValue(QStringLiteral(
"customwidgetstyle"), m_d->customWidgetStyle);
187 settings.setValue(QStringLiteral(
"stylesheetpath"), m_d->styleSheetPath);
188 settings.setValue(QStringLiteral(
"customstylesheet"), m_d->customStyleSheet);
189 settings.setValue(QStringLiteral(
"icontheme"), m_d->iconTheme);
190 settings.setValue(QStringLiteral(
"customicontheme"), m_d->customIconTheme);
191 settings.setValue(QStringLiteral(
"locale"), m_d->localeName);
192 settings.setValue(QStringLiteral(
"customlocale"), m_d->customLocale);
193 settings.setValue(QStringLiteral(
"plugindir"), m_d->additionalPluginDirectory);
194 settings.setValue(QStringLiteral(
"iconthemepath"), m_d->additionalIconThemeSearchPath);
204static QMap<QString, QString> scanIconThemes(
const QStringList &searchPaths)
206 auto res = QMap<QString, QString>();
207 for (
const auto &searchPath : searchPaths) {
208 const auto dir = QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
209 for (
const auto &iconTheme : dir) {
210 auto indexFile = QFile(searchPath % QChar(
'/') % iconTheme % QStringLiteral(
"/index.theme"));
211 auto index = QByteArray();
212 if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) {
213 const auto iconThemeSection = index.indexOf(
"[Icon Theme]");
214 const auto nameStart = index.indexOf(
"Name=", iconThemeSection != -1 ? iconThemeSection : 0);
215 if (nameStart != -1) {
216 auto nameLength = index.indexOf(
"\n", nameStart) - nameStart - 5;
217 if (nameLength > 0) {
218 auto displayName = QString::fromUtf8(index.mid(nameStart + 5, nameLength));
219 if (displayName != iconTheme) {
220 displayName += QChar(
' ') % QChar(
'(') % iconTheme % QChar(
')');
222 res[displayName] = iconTheme;
227 res[iconTheme] = iconTheme;
244void QtSettings::apply()
247 if (m_d->additionalPluginDirectory != m_d->previousPluginDirectory) {
248 if (!m_d->previousPluginDirectory.isEmpty()) {
249 QCoreApplication::removeLibraryPath(m_d->previousPluginDirectory);
251 if (!m_d->additionalPluginDirectory.isEmpty()) {
252 QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory);
254 m_d->previousPluginDirectory = m_d->additionalPluginDirectory;
256 if (m_d->additionalIconThemeSearchPath != m_d->previousIconThemeSearchPath) {
257 auto paths = QIcon::themeSearchPaths();
258 if (!m_d->previousIconThemeSearchPath.isEmpty()) {
259 paths.removeAll(m_d->previousIconThemeSearchPath);
261 if (!m_d->additionalIconThemeSearchPath.isEmpty()) {
262 paths.append(m_d->additionalIconThemeSearchPath);
264 m_d->previousIconThemeSearchPath = m_d->additionalIconThemeSearchPath;
265 QIcon::setThemeSearchPaths(paths);
269 auto styleSheet = QString();
270 if (m_d->customStyleSheet && !m_d->styleSheetPath.isEmpty()) {
271 auto file = QFile(m_d->styleSheetPath);
272 if (!file.open(QFile::ReadOnly)) {
273 std::cerr <<
"Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
275 styleSheet.append(file.readAll());
276 if (file.error() != QFile::NoError) {
277 std::cerr <<
"Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
282 if (m_d->customFont) {
283 if (!m_d->initialFont.has_value()) {
284 m_d->initialFont = QGuiApplication::font();
286 QGuiApplication::setFont(m_d->font);
287 }
else if (m_d->initialFont.has_value()) {
288 QGuiApplication::setFont(m_d->initialFont.value());
290#ifdef QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
291 if (m_d->initialWidgetStyle.isEmpty()) {
294 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) {
295 m_d->initialWidgetStyle = QStringLiteral(
"Fusion");
299 if (m_d->customWidgetStyle) {
300#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
301 const auto *
const currentStyle = QApplication::style();
302 if (m_d->initialWidgetStyle.isEmpty() && currentStyle) {
303 m_d->initialWidgetStyle = currentStyle->name();
306 QApplication::setStyle(m_d->widgetStyle);
307 }
else if (!m_d->initialWidgetStyle.isEmpty()) {
308 QApplication::setStyle(m_d->initialWidgetStyle);
310 if (
auto *
const qapp = qobject_cast<QApplication *>(QApplication::instance())) {
311 qapp->setStyleSheet(styleSheet);
313 std::cerr <<
"Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data()
314 <<
"\" because no QApplication has been instantiated." << std::endl;
316 if (m_d->customPalette) {
317 QGuiApplication::setPalette(m_d->palette);
319 QGuiApplication::setPalette(QPalette());
322 if (m_d->customIconTheme) {
323 QIcon::setThemeName(m_d->iconTheme);
324 }
else if (!m_d->initialIconTheme.isEmpty()) {
325 if (m_d->iconTheme != m_d->initialIconTheme) {
327 QIcon::setThemeName(m_d->initialIconTheme);
336 const auto bundledIconThemes = scanIconThemes(QStringList(QStringLiteral(
":/icons")));
337 if (m_d->isPaletteDark && bundledIconThemes.contains(QStringLiteral(
"default-dark"))) {
338 QIcon::setThemeName(QStringLiteral(
"default-dark"));
339 }
else if (bundledIconThemes.contains(QStringLiteral(
"default"))) {
340 QIcon::setThemeName(QStringLiteral(
"default"));
345 m_d->previousLocale = QLocale();
346 QLocale::setDefault(m_d->customLocale ? QLocale(m_d->localeName) : m_d->defaultLocale);
349 static auto debugInfoLogged =
false;
350 if (debugInfoLogged) {
353 const auto debugLoggingEnabled = CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER
"_LOG_QT_CONFIG");
354 if (debugLoggingEnabled.has_value() && debugLoggingEnabled.value()) {
355 if (
const auto os = QOperatingSystemVersion::current(); os.type() !=
static_cast<decltype(os.type())
>(QOperatingSystemVersion::Unknown)) {
356 const auto version = QVersionNumber(os.majorVersion(), os.minorVersion(), os.microVersion());
357 std::cerr <<
"OS name and version: " << os.name().toStdString() <<
' ' << version.toString().toStdString() <<
'\n';
359 std::cerr <<
"Qt version: " << qVersion() <<
'\n';
360 std::cerr <<
"Qt platform (set QT_QPA_PLATFORM to override): " << QGuiApplication::platformName().toStdString() <<
'\n';
361 std::cerr <<
"Qt locale: " << QLocale().name().toStdString() <<
'\n';
362 std::cerr <<
"Qt library paths: " << QCoreApplication::libraryPaths().join(
':').toStdString() <<
'\n';
363 std::cerr <<
"Qt theme search paths: " << QIcon::themeSearchPaths().join(
':').toStdString() <<
'\n';
365 std::cerr <<
"Is Qt palette dark: " << (m_d->isPaletteDark ?
"yes" :
"no") <<
'\n';
366 debugInfoLogged =
true;
388 if (isPaletteDark == m_d->isPaletteDark) {
392 if (
auto iconTheme = QIcon::themeName(); iconTheme == QStringLiteral(
"default") || iconTheme == QStringLiteral(
"default-dark")) {
393 QIcon::setThemeName(m_d->isPaletteDark ? QStringLiteral(
"default-dark") : QStringLiteral(
"default"));
407void QtSettings::reevaluatePaletteAndDefaultIconTheme()
418bool QtSettings::isPaletteDark()
420 return m_d->isPaletteDark;
426bool QtSettings::hasLocaleChanged()
const
428 return m_d->previousLocale != QLocale();
439OptionCategory *QtSettings::category()
441 auto *category =
new OptionCategory;
442 category->
setDisplayName(QCoreApplication::translate(
"QtGui::QtOptionCategory",
"Qt"));
443 category->
setIcon(QIcon::fromTheme(QStringLiteral(
"qtcreator"), QIcon(QStringLiteral(
":/qtutilities/icons/hicolor/48x48/apps/qtcreator.svg"))));
448QtAppearanceOptionPage::QtAppearanceOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
449 : QtAppearanceOptionPageBase(parentWidget)
450 , m_settings(settings)
451 , m_fontDialog(nullptr)
455QtAppearanceOptionPage::~QtAppearanceOptionPage()
459bool QtAppearanceOptionPage::apply()
461 m_settings.
font = ui()->fontComboBox->currentFont();
462 m_settings.
customFont = !ui()->fontCheckBox->isChecked();
463 m_settings.
widgetStyle = ui()->widgetStyleComboBox->currentText();
465 m_settings.
styleSheetPath = ui()->styleSheetPathSelection->lineEdit()->text();
468 m_settings.
customPalette = !ui()->paletteCheckBox->isChecked();
470 = ui()->iconThemeComboBox->currentIndex() != -1 ? ui()->iconThemeComboBox->currentData().toString() : ui()->iconThemeComboBox->currentText();
475void QtAppearanceOptionPage::reset()
477 ui()->fontComboBox->setCurrentFont(m_settings.
font);
478 ui()->fontCheckBox->setChecked(!m_settings.
customFont);
479 ui()->widgetStyleComboBox->setCurrentText(
480 m_settings.
widgetStyle.isEmpty() ? (QApplication::style() ? QApplication::style()->objectName() : QString()) : m_settings.
widgetStyle);
482 ui()->styleSheetPathSelection->lineEdit()->setText(m_settings.
styleSheetPath);
485 ui()->paletteCheckBox->setChecked(!m_settings.
customPalette);
486 int iconThemeIndex = ui()->iconThemeComboBox->findData(m_settings.
iconTheme);
487 if (iconThemeIndex != -1) {
488 ui()->iconThemeComboBox->setCurrentIndex(iconThemeIndex);
490 ui()->iconThemeComboBox->setCurrentText(m_settings.
iconTheme);
495QWidget *QtAppearanceOptionPage::setupWidget()
498 auto *widget = QtAppearanceOptionPageBase::setupWidget();
504 ui()->widgetStyleComboBox->addItems(QStyleFactory::keys());
507 ui()->styleSheetPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
510 QObject::connect(ui()->fontPushButton, &QPushButton::clicked, widget, [
this] {
512 m_fontDialog =
new QFontDialog(this->widget());
513 m_fontDialog->setCurrentFont(ui()->fontComboBox->font());
514 QObject::connect(m_fontDialog, &QFontDialog::fontSelected, ui()->fontComboBox, &QFontComboBox::setCurrentFont);
515 QObject::connect(ui()->fontComboBox, &QFontComboBox::currentFontChanged, m_fontDialog, &QFontDialog::setCurrentFont);
517 m_fontDialog->show();
521 QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, ui()->paletteToolButton,
525 const auto iconThemes = scanIconThemes(QIcon::themeSearchPaths() << QStringLiteral(
"/usr/share/icons/"));
526 auto *iconThemeComboBox = ui()->iconThemeComboBox;
527 for (
auto i = iconThemes.begin(), end = iconThemes.end(); i != end; ++i) {
528 const auto &displayName = i.key();
529 const auto &
id = i.value();
530 if (
const auto existingItemIndex = iconThemeComboBox->findData(
id); existingItemIndex != -1) {
531 iconThemeComboBox->setItemText(existingItemIndex, displayName);
533 iconThemeComboBox->addItem(displayName,
id);
540QtLanguageOptionPage::QtLanguageOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
541 : QtLanguageOptionPageBase(parentWidget)
542 , m_settings(settings)
546QtLanguageOptionPage::~QtLanguageOptionPage()
550bool QtLanguageOptionPage::apply()
552 m_settings.
localeName = ui()->localeComboBox->currentText();
553 m_settings.
customLocale = !ui()->localeCheckBox->isChecked();
557void QtLanguageOptionPage::reset()
559 ui()->localeComboBox->setCurrentText(m_settings.
localeName);
560 ui()->localeCheckBox->setChecked(!m_settings.
customLocale);
563QWidget *QtLanguageOptionPage::setupWidget()
566 auto *widget = QtLanguageOptionPageBase::setupWidget();
572 auto *localeComboBox = ui()->localeComboBox;
573 const auto locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
574 for (
const QLocale &locale : locales) {
575 localeComboBox->addItem(locale.name());
578 auto *languageLabel = ui()->languageLabel;
579 QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, languageLabel, [languageLabel, localeComboBox] {
580 const auto selectedLocale = QLocale(localeComboBox->currentText());
581 const auto currentLocale = QLocale();
582 const auto territory =
583#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
584 currentLocale.territoryToString(selectedLocale.territory());
586 currentLocale.countryToString(selectedLocale.country());
588 languageLabel->setText(QCoreApplication::translate(
"QtGui::QtLanguageOptionPage",
"recognized by Qt as") % QStringLiteral(
" <i>")
589 % currentLocale.languageToString(selectedLocale.language()) % QChar(
',') % QChar(
' ') % territory % QStringLiteral(
"</i>"));
595 : QtEnvOptionPageBase(parentWidget)
596 , m_settings(settings)
600QtEnvOptionPage::~QtEnvOptionPage()
604bool QtEnvOptionPage::apply()
608 TranslationFiles::additionalTranslationFilePath() = ui()->translationPathSelection->lineEdit()->text();
612void QtEnvOptionPage::reset()
616 ui()->translationPathSelection->lineEdit()->setText(TranslationFiles::additionalTranslationFilePath());
619QWidget *QtEnvOptionPage::setupWidget()
622 return QtEnvOptionPageBase::setupWidget();
631QtSettings::operator QtSettingsData &()
const
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.
QT_UTILITIES_EXPORT QString & additionalTranslationFilePath()
Allows to set an additional search path for translation files.
QtEnvOptionPage(QtSettingsData &settings, QWidget *parentWidget=nullptr)
QT_UTILITIES_EXPORT std::optional< bool > isDarkModeEnabled()
Returns whether dark mode is enabled.
QtAppearanceOptionPage(QtSettingsData &settings, QWidget *parentWidget=nullptr)
QtLanguageOptionPage(QtSettingsData &settings, QWidget *parentWidget=nullptr)
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 initialWidgetStyle
QString additionalPluginDirectory
std::optional< QFont > initialFont
QString additionalIconThemeSearchPath