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();
204 settings.beginGroup(QStringLiteral(
"qt"));
205 settings.setValue(QStringLiteral(
"font"), QVariant(m_d->font.toString()));
206 settings.setValue(QStringLiteral(
"customfont"), m_d->customFont);
207 settings.setValue(QStringLiteral(
"palette"), QVariant(m_d->palette));
208 settings.setValue(QStringLiteral(
"custompalette"), m_d->customPalette);
209#if defined(QT_UTILITIES_GUI_QTWIDGETS)
210 settings.setValue(QStringLiteral(
"widgetstyle"), m_d->widgetStyle);
211 settings.setValue(QStringLiteral(
"customwidgetstyle"), m_d->customWidgetStyle);
212 settings.setValue(QStringLiteral(
"stylesheetpath"), m_d->styleSheetPath);
213 settings.setValue(QStringLiteral(
"customstylesheet"), m_d->customStyleSheet);
215 settings.setValue(QStringLiteral(
"icontheme"), m_d->iconTheme);
216 settings.setValue(QStringLiteral(
"customicontheme"), m_d->customIconTheme);
217 settings.setValue(QStringLiteral(
"locale"), m_d->localeName);
218 settings.setValue(QStringLiteral(
"customlocale"), m_d->customLocale);
219 settings.setValue(QStringLiteral(
"plugindir"), m_d->additionalPluginDirectory);
220 settings.setValue(QStringLiteral(
"iconthemepath"), m_d->additionalIconThemeSearchPath);
230static QMap<QString, QString> scanIconThemes(
const QStringList &searchPaths)
232 auto res = QMap<QString, QString>();
233 for (
const auto &searchPath : searchPaths) {
234 const auto dir = QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
235 for (
const auto &iconTheme : dir) {
236 auto indexFile = QFile(searchPath % QChar(
'/') % iconTheme % QStringLiteral(
"/index.theme"));
237 auto index = QByteArray();
238 if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) {
239 const auto iconThemeSection = index.indexOf(
"[Icon Theme]");
240 const auto nameStart = index.indexOf(
"Name=", iconThemeSection != -1 ? iconThemeSection : 0);
241 if (nameStart != -1) {
242 auto nameLength = index.indexOf(
"\n", nameStart) - nameStart - 5;
243 if (nameLength > 0) {
244 auto displayName = QString::fromUtf8(index.mid(nameStart + 5, nameLength));
245 if (displayName != iconTheme) {
246 displayName += QChar(
' ') % QChar(
'(') % iconTheme % QChar(
')');
248 res[displayName] = iconTheme;
253 res[iconTheme] = iconTheme;
273 if (m_d->additionalPluginDirectory != m_d->previousPluginDirectory) {
274 if (!m_d->previousPluginDirectory.isEmpty()) {
275 QCoreApplication::removeLibraryPath(m_d->previousPluginDirectory);
277 if (!m_d->additionalPluginDirectory.isEmpty()) {
278 QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory);
280 m_d->previousPluginDirectory = m_d->additionalPluginDirectory;
282 if (m_d->additionalIconThemeSearchPath != m_d->previousIconThemeSearchPath) {
283 auto paths = QIcon::themeSearchPaths();
284 if (!m_d->previousIconThemeSearchPath.isEmpty()) {
285 paths.removeAll(m_d->previousIconThemeSearchPath);
287 if (!m_d->additionalIconThemeSearchPath.isEmpty()) {
288 paths.append(m_d->additionalIconThemeSearchPath);
290 m_d->previousIconThemeSearchPath = m_d->additionalIconThemeSearchPath;
291 QIcon::setThemeSearchPaths(paths);
294#if defined(QT_UTILITIES_GUI_QTWIDGETS)
296 auto styleSheet = QString();
297 if (m_d->customStyleSheet && !m_d->styleSheetPath.isEmpty()) {
298 auto file = QFile(m_d->styleSheetPath);
299 if (!file.open(QFile::ReadOnly)) {
300 std::cerr <<
"Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
302 styleSheet.append(file.readAll());
303 if (file.error() != QFile::NoError) {
304 std::cerr <<
"Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() <<
"\"." << std::endl;
310 if (m_d->customFont) {
311 if (!m_d->initialFont.has_value()) {
312 m_d->initialFont = QGuiApplication::font();
314 QGuiApplication::setFont(m_d->font);
315 }
else if (m_d->initialFont.has_value()) {
316 QGuiApplication::setFont(m_d->initialFont.value());
318#if defined(QT_UTILITIES_GUI_QTWIDGETS)
319#ifdef QT_UTILITIES_USE_FUSION_ON_WINDOWS_11
320 if (m_d->initialWidgetStyle.isEmpty()) {
323 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11) {
324 m_d->initialWidgetStyle = QStringLiteral(
"Fusion");
328 if (m_d->customWidgetStyle) {
329#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
330 const auto *
const currentStyle = QApplication::style();
331 if (m_d->initialWidgetStyle.isEmpty() && currentStyle) {
332 m_d->initialWidgetStyle = currentStyle->name();
335 QApplication::setStyle(m_d->widgetStyle);
336 }
else if (!m_d->initialWidgetStyle.isEmpty()) {
337 QApplication::setStyle(m_d->initialWidgetStyle);
339 if (
auto *
const qapp = qobject_cast<QApplication *>(QApplication::instance())) {
340 qapp->setStyleSheet(styleSheet);
342 std::cerr <<
"Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data()
343 <<
"\" because no QApplication has been instantiated." << std::endl;
346 if (m_d->customPalette) {
347 QGuiApplication::setPalette(m_d->palette);
349 QGuiApplication::setPalette(QPalette());
352 if (m_d->customIconTheme) {
353 QIcon::setThemeName(m_d->iconTheme);
354 }
else if (!m_d->initialIconTheme.isEmpty()) {
355 if (m_d->iconTheme != m_d->initialIconTheme) {
357 QIcon::setThemeName(m_d->initialIconTheme);
366 const auto bundledIconThemes = scanIconThemes(QStringList(QStringLiteral(
":/icons")));
367 if (m_d->isPaletteDark && bundledIconThemes.contains(QStringLiteral(
"default-dark"))) {
368 QIcon::setThemeName(QStringLiteral(
"default-dark"));
369 }
else if (bundledIconThemes.contains(QStringLiteral(
"default"))) {
370 QIcon::setThemeName(QStringLiteral(
"default"));
375 m_d->previousLocale = QLocale();
376 QLocale::setDefault(m_d->customLocale ? QLocale(m_d->localeName) : m_d->defaultLocale);
379 static auto debugInfoLogged =
false;
380 if (debugInfoLogged) {
383 const auto debugLoggingEnabled = CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER
"_LOG_QT_CONFIG");
384 if (debugLoggingEnabled.has_value() && debugLoggingEnabled.value()) {
385 if (
const auto os = QOperatingSystemVersion::current(); os.type() !=
static_cast<decltype(os.type())
>(QOperatingSystemVersion::Unknown)) {
386 const auto version = QVersionNumber(os.majorVersion(), os.minorVersion(), os.microVersion());
387 std::cerr <<
"OS name and version: " << os.name().toStdString() <<
' ' << version.toString().toStdString() <<
'\n';
389 std::cerr <<
"Qt version: " << qVersion() <<
'\n';
390 std::cerr <<
"Qt platform (set QT_QPA_PLATFORM to override): " << QGuiApplication::platformName().toStdString() <<
'\n';
391 std::cerr <<
"Qt locale: " << QLocale().name().toStdString() <<
'\n';
392 std::cerr <<
"Qt library paths: " << QCoreApplication::libraryPaths().join(
':').toStdString() <<
'\n';
393 std::cerr <<
"Qt theme search paths: " << QIcon::themeSearchPaths().join(
':').toStdString() <<
'\n';
395 std::cerr <<
"Is Qt palette dark: " << (m_d->isPaletteDark ?
"yes" :
"no") <<
'\n';
396 debugInfoLogged =
true;
422 if (
auto iconTheme = QIcon::themeName(); iconTheme == QStringLiteral(
"default") || iconTheme == QStringLiteral(
"default-dark")) {
423 QIcon::setThemeName(m_d->isPaletteDark ? QStringLiteral(
"default-dark") : QStringLiteral(
"default"));
450 return m_d->isPaletteDark;
458 return m_d->previousLocale != QLocale();
461#if defined(QT_UTILITIES_GUI_QTWIDGETS)
474 category->
setIcon(QIcon::fromTheme(QStringLiteral(
"qtcreator"), QIcon(QStringLiteral(
":/qtutilities/icons/hicolor/48x48/apps/qtcreator.svg"))));
475 category->
assignPages({
new QtAppearanceOptionPage(*m_d),
new QtLanguageOptionPage(*m_d),
new QtEnvOptionPage(*m_d) });
479QtAppearanceOptionPage::QtAppearanceOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
480 : QtAppearanceOptionPageBase(parentWidget)
481 , m_settings(settings)
482 , m_fontDialog(nullptr)
486QtAppearanceOptionPage::~QtAppearanceOptionPage()
490bool QtAppearanceOptionPage::apply()
492 m_settings.font = ui()->fontComboBox->currentFont();
493 m_settings.customFont = !ui()->fontCheckBox->isChecked();
494 m_settings.widgetStyle = ui()->widgetStyleComboBox->currentText();
495 m_settings.customWidgetStyle = !ui()->widgetStyleCheckBox->isChecked();
496 m_settings.styleSheetPath = ui()->styleSheetPathSelection->lineEdit()->text();
497 m_settings.customStyleSheet = !ui()->styleSheetCheckBox->isChecked();
498 m_settings.palette = m_settings.selectedPalette;
499 m_settings.customPalette = !ui()->paletteCheckBox->isChecked();
501 = ui()->iconThemeComboBox->currentIndex() != -1 ? ui()->iconThemeComboBox->currentData().toString() : ui()->iconThemeComboBox->currentText();
502 m_settings.customIconTheme = !ui()->iconThemeCheckBox->isChecked();
506void QtAppearanceOptionPage::reset()
508 ui()->fontComboBox->setCurrentFont(m_settings.font);
509 ui()->fontCheckBox->setChecked(!m_settings.customFont);
510 ui()->widgetStyleComboBox->setCurrentText(
511 m_settings.widgetStyle.isEmpty() ? (QApplication::style() ? QApplication::style()->objectName() : QString()) : m_settings.widgetStyle);
512 ui()->widgetStyleCheckBox->setChecked(!m_settings.customWidgetStyle);
513 ui()->styleSheetPathSelection->lineEdit()->setText(m_settings.styleSheetPath);
514 ui()->styleSheetCheckBox->setChecked(!m_settings.customStyleSheet);
515 m_settings.selectedPalette = m_settings.palette;
516 ui()->paletteCheckBox->setChecked(!m_settings.customPalette);
517 int iconThemeIndex = ui()->iconThemeComboBox->findData(m_settings.iconTheme);
518 if (iconThemeIndex != -1) {
519 ui()->iconThemeComboBox->setCurrentIndex(iconThemeIndex);
521 ui()->iconThemeComboBox->setCurrentText(m_settings.iconTheme);
523 ui()->iconThemeCheckBox->setChecked(!m_settings.customIconTheme);
526QWidget *QtAppearanceOptionPage::setupWidget()
529 auto *widget = QtAppearanceOptionPageBase::setupWidget();
530 if (!m_settings.showNotices) {
535 ui()->widgetStyleComboBox->addItems(QStyleFactory::keys());
538 ui()->styleSheetPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
541 QObject::connect(ui()->fontPushButton, &QPushButton::clicked, widget, [
this] {
543 m_fontDialog =
new QFontDialog(this->widget());
544 m_fontDialog->setCurrentFont(ui()->fontComboBox->font());
545 QObject::connect(m_fontDialog, &QFontDialog::fontSelected, ui()->fontComboBox, &QFontComboBox::setCurrentFont);
546 QObject::connect(ui()->fontComboBox, &QFontComboBox::currentFontChanged, m_fontDialog, &QFontDialog::setCurrentFont);
548 m_fontDialog->show();
552 QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, ui()->paletteToolButton,
556 const auto iconThemes = scanIconThemes(QIcon::themeSearchPaths() << QStringLiteral(
"/usr/share/icons/"));
557 auto *iconThemeComboBox = ui()->iconThemeComboBox;
558 for (
auto i = iconThemes.begin(), end = iconThemes.end(); i != end; ++i) {
559 const auto &displayName = i.key();
560 const auto &
id = i.value();
561 if (
const auto existingItemIndex = iconThemeComboBox->findData(
id); existingItemIndex != -1) {
562 iconThemeComboBox->setItemText(existingItemIndex, displayName);
564 iconThemeComboBox->addItem(displayName,
id);
571QtLanguageOptionPage::QtLanguageOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
572 : QtLanguageOptionPageBase(parentWidget)
573 , m_settings(settings)
577QtLanguageOptionPage::~QtLanguageOptionPage()
581bool QtLanguageOptionPage::apply()
583 m_settings.localeName = ui()->localeComboBox->currentText();
584 m_settings.customLocale = !ui()->localeCheckBox->isChecked();
588void QtLanguageOptionPage::reset()
590 ui()->localeComboBox->setCurrentText(m_settings.localeName);
591 ui()->localeCheckBox->setChecked(!m_settings.customLocale);
594QWidget *QtLanguageOptionPage::setupWidget()
597 auto *widget = QtLanguageOptionPageBase::setupWidget();
598 if (m_settings.retranslatable) {
603 auto *localeComboBox = ui()->localeComboBox;
604 const auto locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
605 for (
const QLocale &locale : locales) {
606 localeComboBox->addItem(locale.name());
609 auto *languageLabel = ui()->languageLabel;
610 QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, languageLabel, [languageLabel, localeComboBox] {
611 const auto selectedLocale = QLocale(localeComboBox->currentText());
612 const auto currentLocale = QLocale();
613 const auto territory =
614#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 0)
615 currentLocale.territoryToString(selectedLocale.territory());
617 currentLocale.countryToString(selectedLocale.country());
619 languageLabel->setText(QCoreApplication::translate(
"QtGui::QtLanguageOptionPage",
"recognized by Qt as") % QStringLiteral(
" <i>")
620 % currentLocale.languageToString(selectedLocale.language()) % QChar(
',') % QChar(
' ') % territory % QStringLiteral(
"</i>"));
625QtEnvOptionPage::QtEnvOptionPage(
QtSettingsData &settings, QWidget *parentWidget)
626 : QtEnvOptionPageBase(parentWidget)
627 , m_settings(settings)
631QtEnvOptionPage::~QtEnvOptionPage()
635bool QtEnvOptionPage::apply()
637 m_settings.additionalPluginDirectory = ui()->pluginPathSelection->lineEdit()->text();
638 m_settings.additionalIconThemeSearchPath = ui()->iconThemeSearchPathSelection->lineEdit()->text();
643void QtEnvOptionPage::reset()
645 ui()->pluginPathSelection->lineEdit()->setText(m_settings.additionalPluginDirectory);
646 ui()->iconThemeSearchPathSelection->lineEdit()->setText(m_settings.additionalIconThemeSearchPath);
650QWidget *QtEnvOptionPage::setupWidget()
653 return QtEnvOptionPageBase::setupWidget();
670#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