diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index dc67ed49..04384e48 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -61,6 +61,20 @@ Singleton { Colorful } + enum TextRenderType { + Qt, + Native, + Curve + } + + enum TextRenderQuality { + Default, + Low, + Normal, + High, + VeryHigh + } + readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation) readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation) readonly property string _configDir: Paths.strip(_configUrl) @@ -483,6 +497,8 @@ Singleton { property int fontWeight: Font.Normal property real fontScale: 1.0 property real dankBarFontScale: 1.0 + property int textRenderType: SettingsData.TextRenderType.Native + property int textRenderQuality: SettingsData.TextRenderQuality.Default property bool notepadUseMonospace: true property string notepadFontFamily: "" diff --git a/quickshell/Common/settings/SettingsSpec.js b/quickshell/Common/settings/SettingsSpec.js index b37ad5ca..ce00dabf 100644 --- a/quickshell/Common/settings/SettingsSpec.js +++ b/quickshell/Common/settings/SettingsSpec.js @@ -242,6 +242,8 @@ var SPEC = { monoFontFamily: { def: "Fira Code" }, fontWeight: { def: 400 }, fontScale: { def: 1.0 }, + textRenderType: { def: 1 }, + textRenderQuality: { def: 0 }, notepadUseMonospace: { def: true }, notepadFontFamily: { def: "" }, diff --git a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml index b03e2969..e06485c9 100644 --- a/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/BluetoothDetail.qml @@ -448,6 +448,7 @@ Rectangle { name: "sync" size: 24 color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.4) + smoothTransform: BluetoothService.adapter?.discovering ?? false RotationAnimator on rotation { running: parent.visible diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index cc27dcea..d074a82a 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -177,6 +177,7 @@ Rectangle { name: "sync" size: 32 color: Theme.primary + smoothTransform: NetworkService.wifiToggling RotationAnimator on rotation { running: NetworkService.wifiToggling @@ -493,6 +494,7 @@ Rectangle { name: "refresh" size: 48 color: Qt.rgba(Theme.surfaceText.r || 0.8, Theme.surfaceText.g || 0.8, Theme.surfaceText.b || 0.8, 0.3) + smoothTransform: wifiScanningOverlay.visible RotationAnimator on rotation { running: wifiScanningOverlay.visible diff --git a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml index db7114da..d6c8e781 100644 --- a/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml +++ b/quickshell/Modules/DankBar/Widgets/SystemUpdate.qml @@ -55,6 +55,7 @@ BasePill { id: statusIcon anchors.centerIn: parent visible: root.isVerticalOrientation + smoothTransform: root.isChecking name: { if (root.isChecking) return "refresh"; @@ -109,6 +110,7 @@ BasePill { DankIcon { id: statusIconHorizontal anchors.verticalCenter: parent.verticalCenter + smoothTransform: root.isChecking name: { if (root.isChecking) return "refresh"; diff --git a/quickshell/Modules/DankDash/WeatherTab.qml b/quickshell/Modules/DankDash/WeatherTab.qml index 510f6746..6d09c4f7 100644 --- a/quickshell/Modules/DankDash/WeatherTab.qml +++ b/quickshell/Modules/DankDash/WeatherTab.qml @@ -146,6 +146,7 @@ Item { color: Theme.withAlpha(Theme.surfaceText, 0.4) anchors.top: parent.top anchors.verticalCenter: parent.verticalCenter + smoothTransform: isRefreshing property bool isRefreshing: false enabled: !isRefreshing @@ -884,6 +885,7 @@ Item { color: Theme.withAlpha(Theme.surfaceText, 0.4) anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter + smoothTransform: isRefreshing property bool isRefreshing: false enabled: !isRefreshing diff --git a/quickshell/Modules/Settings/AudioTab.qml b/quickshell/Modules/Settings/AudioTab.qml index c054791c..cd54c91b 100644 --- a/quickshell/Modules/Settings/AudioTab.qml +++ b/quickshell/Modules/Settings/AudioTab.qml @@ -483,6 +483,7 @@ Item { size: 40 color: Theme.primary anchors.centerIn: parent + smoothTransform: loadingOverlay.visible RotationAnimator { target: spinningIcon diff --git a/quickshell/Modules/Settings/KeybindsTab.qml b/quickshell/Modules/Settings/KeybindsTab.qml index ba46d9bd..804ce27a 100644 --- a/quickshell/Modules/Settings/KeybindsTab.qml +++ b/quickshell/Modules/Settings/KeybindsTab.qml @@ -562,6 +562,7 @@ Item { size: 20 color: Theme.primary anchors.verticalCenter: parent.verticalCenter + smoothTransform: KeybindsService.loading RotationAnimator on rotation { from: 0 diff --git a/quickshell/Modules/Settings/PluginBrowser.qml b/quickshell/Modules/Settings/PluginBrowser.qml index b1f0a6fb..4bc539c8 100644 --- a/quickshell/Modules/Settings/PluginBrowser.qml +++ b/quickshell/Modules/Settings/PluginBrowser.qml @@ -375,6 +375,7 @@ FloatingWindow { size: 48 color: Theme.primary anchors.horizontalCenter: parent.horizontalCenter + smoothTransform: root.isLoading RotationAnimator on rotation { from: 0 diff --git a/quickshell/Modules/Settings/ThemeBrowser.qml b/quickshell/Modules/Settings/ThemeBrowser.qml index 1df32c75..f7659fa2 100644 --- a/quickshell/Modules/Settings/ThemeBrowser.qml +++ b/quickshell/Modules/Settings/ThemeBrowser.qml @@ -343,6 +343,7 @@ FloatingWindow { size: 48 color: Theme.primary anchors.horizontalCenter: parent.horizontalCenter + smoothTransform: root.isLoading RotationAnimator on rotation { from: 0 diff --git a/quickshell/Modules/Settings/TimeWeatherTab.qml b/quickshell/Modules/Settings/TimeWeatherTab.qml index b6c29b7e..53536679 100644 --- a/quickshell/Modules/Settings/TimeWeatherTab.qml +++ b/quickshell/Modules/Settings/TimeWeatherTab.qml @@ -671,6 +671,7 @@ Item { color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4) anchors.right: parent.right anchors.top: parent.top + smoothTransform: isRefreshing property bool isRefreshing: false enabled: !isRefreshing diff --git a/quickshell/Modules/Settings/TypographyMotionTab.qml b/quickshell/Modules/Settings/TypographyMotionTab.qml index 4ad421cd..8d7c9a11 100644 --- a/quickshell/Modules/Settings/TypographyMotionTab.qml +++ b/quickshell/Modules/Settings/TypographyMotionTab.qml @@ -329,6 +329,153 @@ Item { } } + SettingsCard { + tab: "typography" + tags: ["text", "render", "rendering", "quality", "anti-aliasing", "freetype", "distance", "field"] + title: I18n.tr("Text Rendering") + settingKey: "textRenderType" + iconName: "text_format" + + Item { + width: parent.width + height: renderTypeGroup.implicitHeight + clip: true + + DankButtonGroup { + id: renderTypeGroup + anchors.horizontalCenter: parent.horizontalCenter + buttonPadding: parent.width < 480 ? Theme.spacingS : Theme.spacingL + minButtonWidth: parent.width < 480 ? 64 : 96 + textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium + model: ["Native", "Qt", "Curve"] + selectionMode: "single" + currentIndex: { + switch (SettingsData.textRenderType) { + case SettingsData.TextRenderType.Qt: + return 1; + case SettingsData.TextRenderType.Curve: + return 2; + default: + return 0; + } + } + onSelectionChanged: (index, selected) => { + if (!selected) + return; + switch (index) { + case 1: + SettingsData.set("textRenderType", SettingsData.TextRenderType.Qt); + break; + case 2: + SettingsData.set("textRenderType", SettingsData.TextRenderType.Curve); + break; + default: + SettingsData.set("textRenderType", SettingsData.TextRenderType.Native); + break; + } + } + + Connections { + target: SettingsData + function onTextRenderTypeChanged() { + switch (SettingsData.textRenderType) { + case SettingsData.TextRenderType.Qt: + renderTypeGroup.currentIndex = 1; + break; + case SettingsData.TextRenderType.Curve: + renderTypeGroup.currentIndex = 2; + break; + default: + renderTypeGroup.currentIndex = 0; + break; + } + } + } + } + } + + Rectangle { + width: parent.width + height: 1 + color: Theme.outline + opacity: 0.15 + } + + Item { + width: parent.width + height: renderTypeDescription.implicitHeight + Theme.spacingS * 2 + + StyledText { + id: renderTypeDescription + x: Theme.spacingM + y: Theme.spacingS + width: parent.width - Theme.spacingM * 2 + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + wrapMode: Text.WordWrap + text: { + switch (SettingsData.textRenderType) { + case SettingsData.TextRenderType.Qt: + return I18n.tr("Qt: distance-field renderer."); + case SettingsData.TextRenderType.Curve: + return I18n.tr("Curve: curve rasterizer."); + default: + return I18n.tr("Native: platform renderer (FreeType)."); + } + } + } + } + + Rectangle { + width: parent.width + height: 1 + color: Theme.outline + opacity: 0.15 + visible: SettingsData.textRenderType === SettingsData.TextRenderType.Qt + } + + Item { + width: parent.width + height: visible ? qualityGroup.implicitHeight + qualityLabel.implicitHeight + Theme.spacingS : 0 + visible: SettingsData.textRenderType === SettingsData.TextRenderType.Qt + clip: true + + StyledText { + id: qualityLabel + x: Theme.spacingM + text: I18n.tr("Quality") + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: Theme.surfaceText + } + + DankButtonGroup { + id: qualityGroup + anchors.top: qualityLabel.bottom + anchors.topMargin: Theme.spacingS + anchors.horizontalCenter: parent.horizontalCenter + buttonPadding: parent.width < 480 ? Theme.spacingXS : Theme.spacingS + minButtonWidth: parent.width < 480 ? 40 : 56 + textSize: parent.width < 480 ? Theme.fontSizeSmall : Theme.fontSizeMedium + model: ["Default", "Low", "Normal", "High", "Very High"] + selectionMode: "single" + currentIndex: SettingsData.textRenderQuality + onSelectionChanged: (index, selected) => { + if (!selected) + return; + SettingsData.set("textRenderQuality", index); + } + + Connections { + target: SettingsData + function onTextRenderQualityChanged() { + qualityGroup.currentIndex = SettingsData.textRenderQuality; + } + } + } + } + } + SettingsCard { tab: "typography" tags: ["animation", "speed", "motion", "duration"] diff --git a/quickshell/Widgets/DankIcon.qml b/quickshell/Widgets/DankIcon.qml index e80b93a5..ef0676f3 100644 --- a/quickshell/Widgets/DankIcon.qml +++ b/quickshell/Widgets/DankIcon.qml @@ -11,6 +11,7 @@ Item { property real fill: filled ? 1.0 : 0.0 property int grade: Theme.isLightMode ? 0 : -25 property int weight: filled ? 500 : 400 + property bool smoothTransform: false implicitWidth: Math.round(size) implicitHeight: Math.round(size) @@ -28,12 +29,13 @@ Item { anchors.fill: parent font.family: materialSymbolsFont.name - font.pixelSize: Theme.fontSizeMedium + font.pixelSize: Math.round(Theme.fontSizeMedium) font.weight: root.weight + font.hintingPreference: Font.PreferNoHinting color: Theme.surfaceText verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - antialiasing: true + renderType: root.smoothTransform ? Text.QtRendering : Text.NativeRendering Behavior on color { enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None diff --git a/quickshell/Widgets/StyledText.qml b/quickshell/Widgets/StyledText.qml index 3c07ab1f..602ddc6d 100644 --- a/quickshell/Widgets/StyledText.qml +++ b/quickshell/Widgets/StyledText.qml @@ -3,7 +3,6 @@ import qs.Common Text { property bool isMonospace: false - FontLoader { id: interFont source: Qt.resolvedUrl("../assets/fonts/inter/InterVariable.ttf") @@ -24,6 +23,32 @@ Text { return requestedFont; } + readonly property int resolvedRenderType: { + switch (SettingsData.textRenderType) { + case SettingsData.TextRenderType.Qt: + return Text.QtRendering; + case SettingsData.TextRenderType.Curve: + return Text.CurveRendering; + default: + return Text.NativeRendering; + } + } + + readonly property int resolvedRenderQuality: { + switch (SettingsData.textRenderQuality) { + case SettingsData.TextRenderQuality.Low: + return Text.LowRenderTypeQuality; + case SettingsData.TextRenderQuality.Normal: + return Text.NormalRenderTypeQuality; + case SettingsData.TextRenderQuality.High: + return Text.HighRenderTypeQuality; + case SettingsData.TextRenderQuality.VeryHigh: + return Text.VeryHighRenderTypeQuality; + default: + return Text.DefaultRenderTypeQuality; + } + } + readonly property var standardAnimation: { "duration": Appearance.anim.durations.normal, "easing.type": Easing.BezierSpline, @@ -37,7 +62,8 @@ Text { wrapMode: Text.WordWrap elide: Text.ElideRight verticalAlignment: Text.AlignVCenter - //renderType: Text.NativeRendering + renderType: resolvedRenderType + renderTypeQuality: resolvedRenderQuality Behavior on opacity { NumberAnimation {