diff --git a/kcms/fonts/fonts.cpp b/kcms/fonts/fonts.cpp index a27447586..8187ee9af 100644 --- a/kcms/fonts/fonts.cpp +++ b/kcms/fonts/fonts.cpp @@ -1,315 +1,314 @@ /* Copyright 1997 Mark Donohoe Copyright 1999 Lars Knoll Copyright 2000 Rik Hemsley Copyright 2015 Antonis Tsiapaliokas Copyright 2017 Marco Martin Copyright 2019 Benjamin Port Ported to kcontrol2 by Geert Jansen. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "fonts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krdb/krdb.h" #include "previewimageprovider.h" #include "kxftconfig.h" #include "fontssettings.h" #include "fontsaasettings.h" /**** DLL Interface ****/ K_PLUGIN_FACTORY_WITH_JSON(KFontsFactory, "kcm_fonts.json", registerPlugin();) //from KFontRequester // Determine if the font with given properties is available on the system, // otherwise find and return the best fitting combination. static QFont nearestExistingFont(const QFont &font) { QFontDatabase dbase; // Initialize font data according to given font object. QString family = font.family(); QString style = dbase.styleString(font); qreal size = font.pointSizeF(); // Check if the family exists. const QStringList families = dbase.families(); if (!families.contains(family)) { // Chose another family. family = QFontInfo(font).family(); // the nearest match if (!families.contains(family)) { family = families.count() ? families.at(0) : QStringLiteral("fixed"); } } // Check if the family has the requested style. // Easiest by piping it through font selection in the database. QString retStyle = dbase.styleString(dbase.font(family, style, 10)); style = retStyle; // Check if the family has the requested size. // Only for bitmap fonts. if (!dbase.isSmoothlyScalable(family, style)) { QList sizes = dbase.smoothSizes(family, style); if (!sizes.contains(size)) { // Find nearest available size. int mindiff = 1000; int refsize = size; Q_FOREACH (int lsize, sizes) { int diff = qAbs(refsize - lsize); if (mindiff > diff) { mindiff = diff; size = lsize; } } } } // Select the font with confirmed properties. QFont result = dbase.font(family, style, int(size)); if (dbase.isSmoothlyScalable(family, style) && result.pointSize() == floor(size)) { result.setPointSizeF(size); } return result; } /**** KFonts ****/ KFonts::KFonts(QObject *parent, const QVariantList &args) : KQuickAddons::ManagedConfigModule(parent, args) , m_settings(new FontsSettings(this)) , m_settingsAA(new FontsAASettings(this)) , m_subPixelOptionsModel(new QStandardItemModel(this)) , m_hintingOptionsModel(new QStandardItemModel(this)) { KAboutData* about = new KAboutData("kcm_fonts", i18n("Fonts"), "0.1", QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Antonis Tsiapaliokas"), QString(), "antonis.tsiapaliokas@kde.org"); setAboutData(about); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); setButtons(Apply | Default | Help); for (KXftConfig::SubPixel::Type t : {KXftConfig::SubPixel::None, KXftConfig::SubPixel::Rgb, KXftConfig::SubPixel::Bgr, KXftConfig::SubPixel::Vrgb, KXftConfig::SubPixel::Vbgr}) { auto item = new QStandardItem(KXftConfig::description(t)); m_subPixelOptionsModel->appendRow(item); } for (KXftConfig::Hint::Style s : {KXftConfig::Hint::None, KXftConfig::Hint::Slight, KXftConfig::Hint::Medium, KXftConfig::Hint::Full}) { auto item = new QStandardItem(KXftConfig::description(s)); m_hintingOptionsModel->appendRow(item); } connect(m_settingsAA, &FontsAASettings::hintingChanged, this, &KFonts::hintingCurrentIndexChanged); connect(m_settingsAA, &FontsAASettings::subPixelChanged, this, &KFonts::subPixelCurrentIndexChanged); } KFonts::~KFonts() { } FontsSettings *KFonts::fontsSettings() const { return m_settings; } FontsAASettings *KFonts::fontsAASettings() const { return m_settingsAA; } QAbstractItemModel *KFonts::subPixelOptionsModel() const { return m_subPixelOptionsModel; } QAbstractItemModel *KFonts::hintingOptionsModel() const { return m_hintingOptionsModel; } void KFonts::setNearestExistingFonts() { m_settings->setFont(nearestExistingFont(m_settings->font())); m_settings->setFixed(nearestExistingFont(m_settings->fixed())); m_settings->setSmallestReadableFont(nearestExistingFont(m_settings->smallestReadableFont())); m_settings->setToolBarFont(nearestExistingFont(m_settings->toolBarFont())); m_settings->setMenuFont(nearestExistingFont(m_settings->menuFont())); m_settings->setActiveFont(nearestExistingFont(m_settings->activeFont())); } void KFonts::load() { // first load all the settings ManagedConfigModule::load(); // Then set the existing fonts based on those settings setNearestExistingFonts(); // Load preview // NOTE: This needs to be done AFTER AA settings is loaded // otherwise AA settings will be reset in process of loading // previews engine()->addImageProvider("preview", new PreviewImageProvider(m_settings->font())); // KCM expect save state to be false at this point (can be true because of setNearestExistingFonts setNeedsSave(false); } void KFonts::save() { auto dpiItem = m_settingsAA->findItem("forceFontDPI"); auto dpiWaylandItem = m_settingsAA->findItem("forceFontDPIWayland"); auto antiAliasingItem = m_settingsAA->findItem("antiAliasing"); Q_ASSERT(dpiItem && dpiWaylandItem && antiAliasingItem); if (dpiItem->isSaveNeeded() || dpiWaylandItem->isSaveNeeded() || antiAliasingItem->isSaveNeeded()) { emit aliasingChangeApplied(); } auto forceFontDPIChanged = dpiItem->isSaveNeeded(); ManagedConfigModule::save(); #if HAVE_X11 // if the setting is reset in the module, remove the dpi value, // otherwise don't explicitly remove it and leave any possible system-wide value if (m_settingsAA->forceFontDPI() == 0 && forceFontDPIChanged && !KWindowSystem::isPlatformWayland()) { QProcess proc; proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.start("xrdb", QStringList() << "-quiet" << "-remove" << "-nocpp"); if (proc.waitForStarted()) { proc.write(QByteArray("Xft.dpi\n")); proc.closeWriteChannel(); proc.waitForFinished(); } } QApplication::processEvents(); #endif KGlobalSettings::self()->emitChange(KGlobalSettings::FontChanged); runRdb(KRdbExportXftSettings | KRdbExportGtkTheme); - - emit fontsHaveChanged(); } void KFonts::adjustFont(const QFont &font, const QString &category) { QFont selFont = font; int ret = KFontChooserDialog::getFont(selFont, KFontChooser::NoDisplayFlags); if (ret == QDialog::Accepted) { if (category == QLatin1String("font")) { m_settings->setFont(selFont); } else if (category == QLatin1String("menuFont")) { m_settings->setMenuFont(selFont); } else if (category == QLatin1String("toolBarFont")) { m_settings->setToolBarFont(selFont); } else if (category == QLatin1String("activeFont")) { m_settings->setActiveFont(selFont); } else if (category == QLatin1String("smallestReadableFont")) { m_settings->setSmallestReadableFont(selFont); } else if (category == QLatin1String("fixed")) { m_settings->setFixed(selFont); } } + emit fontsHaveChanged(); } void KFonts::adjustAllFonts() { QFont font = m_settings->font(); KFontChooser::FontDiffFlags fontDiffFlags; int ret = KFontChooserDialog::getFontDiff(font, fontDiffFlags, KFontChooser::NoDisplayFlags); if (ret == QDialog::Accepted && fontDiffFlags) { m_settings->setFont(applyFontDiff(m_settings->font(), font, fontDiffFlags)); m_settings->setMenuFont(applyFontDiff(m_settings->menuFont(), font, fontDiffFlags)); m_settings->setToolBarFont(applyFontDiff(m_settings->toolBarFont(), font, fontDiffFlags)); m_settings->setActiveFont(applyFontDiff(m_settings->activeFont(), font, fontDiffFlags)); m_settings->setSmallestReadableFont(applyFontDiff(m_settings->smallestReadableFont(), font, fontDiffFlags)); const QFont adjustedFont = applyFontDiff(m_settings->fixed(), font, fontDiffFlags); if (QFontInfo(adjustedFont).fixedPitch()) { m_settings->setFixed(adjustedFont); } } } QFont KFonts::applyFontDiff(const QFont &fnt, const QFont &newFont, int fontDiffFlags) { QFont font(fnt); if (fontDiffFlags & KFontChooser::FontDiffSize) { font.setPointSizeF(newFont.pointSizeF()); } if ((fontDiffFlags & KFontChooser::FontDiffFamily)) { font.setFamily(newFont.family()); } if (fontDiffFlags & KFontChooser::FontDiffStyle) { font.setWeight(newFont.weight()); font.setStyle(newFont.style()); font.setUnderline(newFont.underline()); font.setStyleName(newFont.styleName()); } return font; } int KFonts::subPixelCurrentIndex() const { return m_settingsAA->subPixel() - KXftConfig::SubPixel::None; } void KFonts::setSubPixelCurrentIndex(int idx) { m_settingsAA->setSubPixel(static_cast(KXftConfig::SubPixel::None + idx)); } int KFonts::hintingCurrentIndex() const { return m_settingsAA->hinting() - KXftConfig::Hint::None; } void KFonts::setHintingCurrentIndex(int idx) { m_settingsAA->setHinting(static_cast(KXftConfig::Hint::None + idx)); } #include "fonts.moc" diff --git a/kcms/fonts/fonts.h b/kcms/fonts/fonts.h index f3bfa23b4..c5189fd27 100644 --- a/kcms/fonts/fonts.h +++ b/kcms/fonts/fonts.h @@ -1,89 +1,90 @@ /* Copyright 1997 Mark Donohoe Copyright 1999 Lars Knoll Copyright 2000 Rik Hemsley Copyright 2015 Antonis Tsiapaliokas Copyright 2017 Marco Martin Copyright 2019 Benjamin Port Ported to kcontrol2 by Geert Jansen. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FONTS_H #define FONTS_H #include #include #include #include class FontsSettings; class FontsAASettings; /** * The Desktop/fonts tab in kcontrol. */ class KFonts : public KQuickAddons::ManagedConfigModule { Q_OBJECT Q_PROPERTY(FontsSettings *fontsSettings READ fontsSettings CONSTANT) Q_PROPERTY(FontsAASettings *fontsAASettings READ fontsAASettings CONSTANT) Q_PROPERTY(QAbstractItemModel *subPixelOptionsModel READ subPixelOptionsModel CONSTANT) Q_PROPERTY(int subPixelCurrentIndex READ subPixelCurrentIndex WRITE setSubPixelCurrentIndex NOTIFY subPixelCurrentIndexChanged) Q_PROPERTY(QAbstractItemModel *hintingOptionsModel READ hintingOptionsModel CONSTANT) Q_PROPERTY(int hintingCurrentIndex READ hintingCurrentIndex WRITE setHintingCurrentIndex NOTIFY hintingCurrentIndexChanged) public: KFonts(QObject *parent, const QVariantList &); ~KFonts() override; FontsSettings *fontsSettings() const; FontsAASettings *fontsAASettings() const; int subPixelCurrentIndex() const; void setHintingCurrentIndex(int idx); int hintingCurrentIndex() const; void setSubPixelCurrentIndex(int idx); QAbstractItemModel *subPixelOptionsModel() const; QAbstractItemModel *hintingOptionsModel() const; public Q_SLOTS: void load() override; void save() override; Q_INVOKABLE void adjustAllFonts(); Q_INVOKABLE void adjustFont(const QFont &font, const QString &category); Q_SIGNALS: void fontsHaveChanged(); void hintingCurrentIndexChanged(); void subPixelCurrentIndexChanged(); void aliasingChangeApplied(); + void fontDpiSettingsChanged(); private: QFont applyFontDiff(const QFont &fnt, const QFont &newFont, int fontDiffFlags); void setNearestExistingFonts(); FontsSettings *m_settings; FontsAASettings *m_settingsAA; QStandardItemModel *m_subPixelOptionsModel; QStandardItemModel *m_hintingOptionsModel; }; #endif diff --git a/kcms/fonts/package/contents/ui/main.qml b/kcms/fonts/package/contents/ui/main.qml index 116179d50..b4da60b49 100644 --- a/kcms/fonts/package/contents/ui/main.qml +++ b/kcms/fonts/package/contents/ui/main.qml @@ -1,277 +1,326 @@ /* Copyright (c) 2015 Antonis Tsiapaliokas Copyright (c) 2017 Marco Martin Copyright (c) 2019 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.0 as QtControls import QtQuick.Dialogs 1.2 as QtDialogs + +// For KCMShell.open() +import org.kde.kquickcontrolsaddons 2.0 import org.kde.kirigami 2.4 as Kirigami import org.kde.kcm 1.1 as KCM KCM.SimpleKCM { id: root KCM.ConfigModule.quickHelp: i18n("This module lets you configure the system fonts.") + property var kscreenAction: Kirigami.Action { + visible: KCMShell.authorize("kcm_kscreen.desktop").length > 0 + text: i18n("Change Display Scaling...") + iconName: "preferences-desktop-display" + onTriggered: KCMShell.open("kcm_kscreen.desktop") + } + ColumnLayout { Kirigami.InlineMessage { id: antiAliasingMessage Layout.fillWidth: true showCloseButton: true text: i18n("Some changes such as anti-aliasing or DPI will only affect newly started applications.") Connections { target: kcm onAliasingChangeApplied: antiAliasingMessage.visible = true } } + Kirigami.InlineMessage { + id: hugeFontsMessage + Layout.fillWidth: true + showCloseButton: true + text: i18n("Very large fonts may produce odd-looking results. Consider adjusting the global screen scale instead of using a very large font size.") + + Connections { + target: kcm + onFontsHaveChanged: { + if (generalFontWidget.font.pointSize > 14 + || fixedWidthFontWidget.font.pointSize > 14 + || smallFontWidget.font.pointSize > 14 + || toolbarFontWidget.font.pointSize > 14 + || menuFontWidget.font.pointSize > 18) { + hugeFontsMessage.visible = true + } else { + hugeFontsMessage.visible = false + } + } + } + + actions: [ kscreenAction ] + } + + Kirigami.InlineMessage { + id: dpiTwiddledMessage + Layout.fillWidth: true + showCloseButton: true + text: i18n("The recommended way to scale the user interface is using the global screen scaling feature.") + actions: [ kscreenAction ] + } + Kirigami.FormLayout { id: formLayout readonly property int maxImplicitWidth: Math.max(adjustAllFontsButton.implicitWidth, excludeField.implicitWidth, subPixelCombo.implicitWidth, hintingCombo.implicitWidth) QtControls.Button { id: adjustAllFontsButton Layout.preferredWidth: formLayout.maxImplicitWidth icon.name: "font-select-symbolic" text: i18n("&Adjust All Fonts...") onClicked: kcm.adjustAllFonts(); enabled: !kcm.fontsSettings.isImmutable("font") || !kcm.fontsSettings.isImmutable("fixed") || !kcm.fontsSettings.isImmutable("smallestReadableFont") || !kcm.fontsSettings.isImmutable("toolBarFont") || !kcm.fontsSettings.isImmutable("menuFont") || !kcm.fontsSettings.isImmutable("activeFont") } FontWidget { id: generalFontWidget label: i18n("General:") category: "font" font: kcm.fontsSettings.font enabled: !kcm.fontsSettings.isImmutable("font") } FontWidget { + id: fixedWidthFontWidget label: i18n("Fixed width:") category: "fixed" font: kcm.fontsSettings.fixed enabled: !kcm.fontsSettings.isImmutable("fixed") } FontWidget { + id: smallFontWidget label: i18n("Small:") category: "smallestReadableFont" font: kcm.fontsSettings.smallestReadableFont enabled: !kcm.fontsSettings.isImmutable("smallestReadableFont") } FontWidget { + id: toolbarFontWidget label: i18n("Toolbar:") category: "toolBarFont" font: kcm.fontsSettings.toolBarFont enabled: !kcm.fontsSettings.isImmutable("toolBarFont") } FontWidget { + id: menuFontWidget label: i18n("Menu:") category: "menuFont" font: kcm.fontsSettings.menuFont enabled: !kcm.fontsSettings.isImmutable("menuFont") } FontWidget { label: i18n("Window title:") category: "activeFont" font: kcm.fontsSettings.activeFont enabled: !kcm.fontsSettings.isImmutable("activeFont") } Kirigami.Separator { Kirigami.FormData.isSection: true } QtControls.CheckBox { id: antiAliasingCheckBox checked: kcm.fontsAASettings.antiAliasing onCheckedChanged: kcm.fontsAASettings.antiAliasing = checked Kirigami.FormData.label: i18n("Anti-Aliasing:") text: i18n("Enable") Layout.fillWidth: true } QtControls.CheckBox { id: excludeCheckBox checked: kcm.fontsAASettings.exclude onCheckedChanged: kcm.fontsAASettings.exclude = checked; text: i18n("Exclude range from anti-aliasing") Layout.fillWidth: true enabled: antiAliasingCheckBox.checked } RowLayout { id: excludeField Layout.preferredWidth: formLayout.maxImplicitWidth enabled: antiAliasingCheckBox.checked QtControls.SpinBox { id: excludeFromSpinBox stepSize: 1 onValueChanged: kcm.fontsAASettings.excludeFrom = value textFromValue: function(value, locale) { return i18n("%1 pt", value)} valueFromText: function(text, locale) { return parseInt(text) } editable: true enabled: excludeCheckBox.checked value: kcm.fontsAASettings.excludeFrom } QtControls.Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter text: i18n("to") enabled: excludeCheckBox.checked } QtControls.SpinBox { id: excludeToSpinBox stepSize: 1 onValueChanged: kcm.fontsAASettings.excludeTo = value textFromValue: function(value, locale) { return i18n("%1 pt", value)} valueFromText: function(text, locale) { return parseInt(text) } editable: true enabled: excludeCheckBox.checked value: kcm.fontsAASettings.excludeTo } Connections { target: kcm.fontsAASettings onExcludeFromChanged: excludeFromSpinBox.value = kcm.fontsAASettings.excludeFrom; onExcludeToChanged: excludeToSpinBox.value = kcm.fontsAASettings.excludeTo; } } QtControls.ComboBox { id: subPixelCombo Layout.preferredWidth: formLayout.maxImplicitWidth Kirigami.FormData.label: i18nc("Used as a noun, and precedes a combobox full of options", "Sub-pixel rendering:") currentIndex: kcm.subPixelCurrentIndex onCurrentIndexChanged: kcm.subPixelCurrentIndex = currentIndex; model: kcm.subPixelOptionsModel textRole: "display" enabled: antiAliasingCheckBox.checked popup.height: popup.implicitHeight delegate: QtControls.ItemDelegate { id: subPixelDelegate onWidthChanged: { subPixelCombo.popup.width = Math.max(subPixelCombo.popup.width, width) } contentItem: ColumnLayout { id: subPixelLayout Kirigami.Heading { id: subPixelComboText text: model.display level: 5 } Image { id: subPixelComboImage source: "image://preview/" + model.index + "_" + kcm.hintingCurrentIndex + ".png" // Setting sourceSize here is necessary as a workaround for QTBUG-38127 // // With this bug, images requested from a QQuickImageProvider have an incorrect scale with devicePixelRatio != 1 when sourceSize is not set. // // TODO: Check if QTBUG-38127 is fixed and remove the next two lines. sourceSize.width: 1 sourceSize.height: 1 } } } } QtControls.ComboBox { id: hintingCombo Layout.preferredWidth: formLayout.maxImplicitWidth Kirigami.FormData.label: i18nc("Used as a noun, and precedes a combobox full of options", "Hinting:") currentIndex: kcm.hintingCurrentIndex onCurrentTextChanged: kcm.hintingCurrentIndex = currentIndex; model: kcm.hintingOptionsModel textRole: "display" enabled: antiAliasingCheckBox.checked popup.height: popup.implicitHeight delegate: QtControls.ItemDelegate { id: hintingDelegate onWidthChanged: { hintingCombo.popup.width = Math.max(hintingCombo.popup.width, width) } contentItem: ColumnLayout { id: hintingLayout Kirigami.Heading { id: hintingComboText text: model.display level: 5 } Image { id: hintingComboImage source: "image://preview/" + kcm.subPixelCurrentIndex + "_" + model.index + ".png" // Setting sourceSize here is necessary as a workaround for QTBUG-38127 // // With this bug, images requested from a QQuickImageProvider have an incorrect scale with devicePixelRatio != 1 when sourceSize is not set. // // TODO: Check if QTBUG-38127 is fixed and remove the next two lines. sourceSize.width: 1 sourceSize.height: 1 } } } } RowLayout { QtControls.CheckBox { id: dpiCheckBox checked: kcm.fontsAASettings.dpi !== 0 text: i18n("Force font DPI:") - onClicked: kcm.fontsAASettings.dpi = (checked ? dpiSpinBox.value : 0) + onClicked: { + kcm.fontsAASettings.dpi = checked ? dpiSpinBox.value : 0 + dpiTwiddledMessage.visible = checked + } } QtControls.SpinBox { id: dpiSpinBox stepSize: 24 editable: true enabled: dpiCheckBox.checked value: kcm.fontsAASettings.dpi !== 0 ? kcm.fontsAASettings.dpi : 96 onValueModified: kcm.fontsAASettings.dpi = value // to: need to divide to stepSize to: 1008 // lowest vaue here can be == stepSize, that is because 0 means off from: 24 } } QtDialogs.FontDialog { id: fontDialog title: i18n("Select Font") modality: Qt.WindowModal property string currentCategory property bool adjustAllFonts: false onAccepted: { if (adjustAllFonts) { kcm.adjustAllFonts() } else { kcm.adjustFont(font, currentCategory) } } } } } }