diff --git a/Modules/Demo/DemoPanel.qml b/Modules/Demo/DemoPanel.qml index 6710202..5d59e23 100644 --- a/Modules/Demo/DemoPanel.qml +++ b/Modules/Demo/DemoPanel.qml @@ -62,22 +62,18 @@ NLoader { } RowLayout { spacing: Style.marginSmall * scaling - NText { - text: `${Math.round(Scaling.overrideScale * 100)}%` - Layout.alignment: Qt.AlignVCenter - } NSlider { - id: scaleSlider - from: 0.6 - to: 1.8 - stepSize: 0.01 - value: Scaling.overrideScale + label: "Scaling" + description: "Scaling goes brrrr" + valueSuffix: "%" + from: 60 + to: 180 + stepSize: 1 + value: Scaling.overrideScale * 100 implicitWidth: bgRect.width * 0.75 - onMoved: { - Scaling.overrideScale = value - } - onPressedChanged: { + onPressedChanged: function (pressed, value) { Scaling.overrideEnabled = true + Scaling.overrideScale = value / 100 } } NIconButton { @@ -86,6 +82,7 @@ NLoader { onClicked: { Scaling.overrideEnabled = false Scaling.overrideScale = 1.0 + console.log("Reset!") } } } diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index a9f0ca5..856c2b6 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -7,7 +7,6 @@ import qs.Modules.Settings.Tabs as Tabs import qs.Services import qs.Widgets - NLoader { id: root diff --git a/Modules/Settings/Tabs/Wallpaper.qml b/Modules/Settings/Tabs/Wallpaper.qml index 36c4816..59eb6bf 100644 --- a/Modules/Settings/Tabs/Wallpaper.qml +++ b/Modules/Settings/Tabs/Wallpaper.qml @@ -116,49 +116,19 @@ ColumnLayout { } // Wallpaper Interval - ColumnLayout { - spacing: 8 + NSlider { + label: "Wallpaper Interval" + description: "How often to change wallpapers automatically (in seconds)" + valueSuffix: "s" + from: 10 + to: 900 + stepSize: 10 + value: Settings.data.wallpaper.randomInterval + onPressedChanged: function (pressed, value) { + Settings.data.wallpaper.randomInterval = Math.round(value) + } + cutoutColor: Colors.backgroundPrimary Layout.fillWidth: true - Layout.topMargin: 8 - - NText { - text: "Wallpaper Interval" - font.pointSize: 13 - font.weight: Style.fontWeightBold - color: Colors.textPrimary - } - - NText { - text: "How often to change wallpapers automatically (in seconds)" - font.pointSize: 12 - color: Colors.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - NText { - text: Settings.data.wallpaper.randomInterval + " seconds" - font.pointSize: 13 - color: Colors.textPrimary - } - - Item { - Layout.fillWidth: true - } - } - - NSlider { - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.data.wallpaper.randomInterval - onPressedChanged: Settings.data.wallpaper.randomInterval = Math.round(value) - cutoutColor: Colors.backgroundPrimary - } } } @@ -259,95 +229,35 @@ ColumnLayout { } // Transition FPS - ColumnLayout { - spacing: 8 + NSlider { + label: "Transition FPS" + description: "Frames per second for transition animations" + valueSuffix: " FPS" + from: 30 + to: 500 + stepSize: 5 + value: Settings.data.wallpaper.swww.transitionFps + onPressedChanged: function (pressed, value) { + Settings.data.wallpaper.swww.transitionFps = Math.round(value) + } + cutoutColor: Colors.backgroundPrimary Layout.fillWidth: true - Layout.topMargin: 8 - - NText { - text: "Transition FPS" - font.pointSize: 13 - font.weight: Style.fontWeightBold - color: Colors.textPrimary - } - - NText { - text: "Frames per second for transition animations" - font.pointSize: 12 - color: Colors.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - NText { - text: Settings.data.wallpaper.swww.transitionFps + " FPS" - font.pointSize: 13 - color: Colors.textPrimary - } - - Item { - Layout.fillWidth: true - } - } - - NSlider { - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.data.wallpaper.swww.transitionFps - onPressedChanged: Settings.data.wallpaper.swww.transitionFps = Math.round(value) - cutoutColor: Colors.backgroundPrimary - } } // Transition Duration - ColumnLayout { - spacing: 8 + NSlider { + label: "Transition Duration" + description: "Duration of transition animations in seconds" + valueSuffix: "s" + from: 0.25 + to: 10 + stepSize: 0.05 + value: Settings.data.wallpaper.swww.transitionDuration + onPressedChanged: function (pressed, value) { + Settings.data.wallpaper.swww.transitionDuration = value + } + cutoutColor: Colors.backgroundPrimary Layout.fillWidth: true - Layout.topMargin: 8 - - NText { - text: "Transition Duration" - font.pointSize: 13 - font.weight: Style.fontWeightBold - color: Colors.textPrimary - } - - NText { - text: "Duration of transition animations in seconds" - font.pointSize: 12 - color: Colors.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - NText { - text: Settings.data.wallpaper.swww.transitionDuration.toFixed(3) + " seconds" - font.pointSize: 13 - color: Colors.textPrimary - } - - Item { - Layout.fillWidth: true - } - } - - NSlider { - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.data.wallpaper.swww.transitionDuration - onPressedChanged: Settings.data.wallpaper.swww.transitionDuration = value - cutoutColor: Colors.backgroundPrimary - } } } } diff --git a/Widgets/NSlider.qml b/Widgets/NSlider.qml index 2e9e6d1..4911099 100644 --- a/Widgets/NSlider.qml +++ b/Widgets/NSlider.qml @@ -1,9 +1,10 @@ import QtQuick import QtQuick.Controls import QtQuick.Effects +import QtQuick.Layouts import qs.Services -Slider { +ColumnLayout { id: root readonly property real scaling: Scaling.scale(screen) @@ -11,80 +12,156 @@ Slider { readonly property real trackHeight: knobDiameter * 0.5 readonly property real cutoutExtra: Style.baseWidgetSize * 0.1 * scaling + property string label: "" + property string description: "" + property string valueSuffix: "" + property real from: 0.0 + property real to: 1.0 + property real stepSize: 0.01 + property real value: 0.0 + // Optional color to cut the track beneath the knob (should match surrounding background) property var cutoutColor property var screen property bool snapAlways: true - snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease - implicitHeight: Math.max(trackHeight, knobDiameter) + signal pressedChanged(bool pressed, real value) + signal moved(real value) - background: Rectangle { - x: root.leftPadding - y: root.topPadding + root.availableHeight / 2 - height / 2 - implicitWidth: Style.sliderWidth - implicitHeight: trackHeight - width: root.availableWidth - height: implicitHeight - radius: height / 2 - color: Colors.surfaceVariant + Layout.fillWidth: true + spacing: Style.marginSmall * scaling - Rectangle { - id: activeTrack - width: root.visualPosition * parent.width - height: parent.height - color: Colors.accentPrimary - radius: parent.radius + RowLayout { + Layout.fillWidth: true + + ColumnLayout { + spacing: Style.marginTiniest * scaling + + NText { + text: label + font.pointSize: Style.fontSizeMedium * scaling + font.weight: Style.fontWeightBold + color: Colors.textPrimary + Layout.fillWidth: true + } + + NText { + text: description + font.pointSize: Style.fontSizeSmall * scaling + color: Colors.textSecondary + wrapMode: Text.WordWrap + } } - // Circular cutout - Rectangle { - id: knobCutout - width: knobDiameter + cutoutExtra - height: knobDiameter + cutoutExtra - radius: width / 2 - color: root.cutoutColor !== undefined ? root.cutoutColor : Colors.backgroundPrimary - x: Math.max(0, Math.min(parent.width - width, - root.visualPosition * (parent.width - root.knobDiameter) - cutoutExtra / 2)) - y: (parent.height - height) / 2 + NText { + text: { + var v + if (Number.isInteger(value)) { + v = value + } else { + v = value.toFixed(2) + } + + if (valueSuffix != "") { + return v + valueSuffix + } else { + return v + } + } + font.pointSize: Style.fontSizeMedium * scaling + font.weight: Style.fontWeightBold + color: Colors.textPrimary + Layout.alignment: Qt.AlignBottom | Qt.AlignRight + } } - handle: Item { - width: knob.implicitWidth - height: knob.implicitHeight - x: root.leftPadding + root.visualPosition * (root.availableWidth - width) - y: root.topPadding + root.availableHeight / 2 - height / 2 + Slider { + id: slider - // Subtle shadow for a more polished look (keeps theme colors) - MultiEffect { - anchors.fill: knob - source: knob - shadowEnabled: true - shadowColor: Colors.shadow - shadowOpacity: 0.25 - shadowHorizontalOffset: 0 - shadowVerticalOffset: 1 - shadowBlur: 8 + Layout.fillWidth: true + from: root.from + to: root.to + stepSize: root.stepSize + value: root.value + snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease + implicitWidth: root.width + implicitHeight: Math.max(trackHeight, knobDiameter) + onPressedChanged: { + root.pressedChanged(slider.pressed, slider.value) + } + onMoved: { + root.value = slider.value + root.moved(value) } - Rectangle { - id: knob - implicitWidth: knobDiameter - implicitHeight: knobDiameter - radius: width * 0.5 - color: root.pressed ? Colors.surfaceVariant : Colors.surface - border.color: Colors.accentPrimary - border.width: Math.max(1, Style.borderThick * scaling) + background: Rectangle { + x: slider.leftPadding + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + implicitWidth: Style.sliderWidth + implicitHeight: trackHeight + width: slider.availableWidth + height: implicitHeight + radius: height / 2 + color: Colors.surfaceVariant - // Press feedback halo (using accent color, low opacity) Rectangle { - anchors.centerIn: parent - width: parent.width + 8 * scaling - height: parent.height + 8 * scaling - radius: width / 2 + id: activeTrack + width: slider.visualPosition * parent.width + height: parent.height color: Colors.accentPrimary - opacity: root.pressed ? 0.16 : 0.0 + radius: parent.radius + } + + // Circular cutout + Rectangle { + id: knobCutout + width: knobDiameter + cutoutExtra + height: knobDiameter + cutoutExtra + radius: width / 2 + color: slider.cutoutColor !== undefined ? slider.cutoutColor : Colors.backgroundPrimary + x: Math.max(0, Math.min(parent.width - width, + slider.visualPosition * (parent.width - knobDiameter) - cutoutExtra / 2)) + y: (parent.height - height) / 2 + } + } + + handle: Item { + width: knob.implicitWidth + height: knob.implicitHeight + x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + + // Subtle shadow for a more polished look (keeps theme colors) + MultiEffect { + anchors.fill: knob + source: knob + shadowEnabled: true + shadowColor: Colors.shadow + shadowOpacity: 0.25 + shadowHorizontalOffset: 0 + shadowVerticalOffset: 1 + shadowBlur: 8 + } + + Rectangle { + id: knob + implicitWidth: knobDiameter + implicitHeight: knobDiameter + radius: width * 0.5 + color: slider.pressed ? Colors.surfaceVariant : Colors.surface + border.color: Colors.accentPrimary + border.width: Math.max(1, Style.borderThick * scaling) + + // Press feedback halo (using accent color, low opacity) + Rectangle { + anchors.centerIn: parent + width: parent.width + 8 * scaling + height: parent.height + 8 * scaling + radius: width / 2 + color: Colors.accentPrimary + opacity: slider.pressed ? 0.16 : 0.0 + } } } }