Files
noctalia-shell/Widgets/NColorSlider.qml
T
anthonyhab 353e4e5409 fix(slider): enable layer rendering for opacity animations
Add layer.enabled: true to NColorSlider and NSlider widgets to fix
opacity animation issues. This ensures proper compositing during
slider animations by rendering to an offscreen layer.

Fixes visual glitches where slider opacity transitions were not
rendering correctly.
2026-01-29 13:21:07 -05:00

180 lines
4.9 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import qs.Commons
import qs.Services.UI
Slider {
id: root
property color fillColor: "transparent"
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real widthRatio: 0.7
property string tooltipText
property string tooltipDirection: "auto"
property bool hovering: false
property color topColor: "white"
property color bottomColor: "black"
property bool rainbowMode: false
readonly property real knobDiameter: Math.round((Style.baseWidgetSize * widthRatio * Style.uiScaleRatio) / 2) * 2
readonly property real trackWidth: Math.round((knobDiameter * 0.4 * Style.uiScaleRatio) / 2) * 2
readonly property real cutoutExtra: Math.round((Style.baseWidgetSize * 0.1 * Style.uiScaleRatio) / 2) * 2
//horizontal: false
orientation: Qt.Vertical
padding: cutoutExtra / 2
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
implicitWidth: Math.max(trackWidth, knobDiameter)
background: Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
x: root.leftPadding + root.availableWidth / 2 - width / 2
height: root.availableHeight - root.knobDiameter
width: root.trackWidth
radius: Math.min(Style.iRadiusL, width / 2)
color: Qt.alpha(Color.mSurface, 0.5)
border.color: Qt.alpha(Color.mOutline, 0.5)
border.width: Style.borderS
// Gradients
gradient: root.rainbowMode ? rainbowGradient : standardGradient
Gradient {
id: standardGradient
orientation: Gradient.Vertical
GradientStop {
position: 0.0
color: root.topColor
}
GradientStop {
position: 1.0
color: root.bottomColor
}
}
Gradient {
id: rainbowGradient
orientation: Gradient.Vertical
GradientStop {
position: 0.000
color: "#FF0000"
}
GradientStop {
position: 0.167
color: "#FF00FF"
}
GradientStop {
position: 0.333
color: "#0000FF"
}
GradientStop {
position: 0.500
color: "#00FFFF"
}
GradientStop {
position: 0.667
color: "#00FF00"
}
GradientStop {
position: 0.833
color: "#FFFF00"
}
GradientStop {
position: 1.000
color: "#FF0000"
}
}
// Circular cutout
Rectangle {
id: knobCutout
implicitWidth: root.knobDiameter + root.cutoutExtra
implicitHeight: root.knobDiameter + root.cutoutExtra
radius: Math.min(Style.iRadiusL, width / 2)
color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface
y: root.visualPosition * (root.availableHeight - root.knobDiameter) - ((root.knobDiameter + root.cutoutExtra) / 2)
anchors.horizontalCenter: parent.horizontalCenter
}
}
handle: Item {
implicitWidth: root.knobDiameter
implicitHeight: root.knobDiameter
y: root.topPadding + root.visualPosition * (root.availableHeight - height)
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
id: knob
implicitWidth: root.knobDiameter
implicitHeight: root.knobDiameter
radius: Math.min(Style.iRadiusL, width / 2)
color: {
if (root.rainbowMode) {
// Hue Logic: Map position (0.0 to 1.0) directly to Hue
return Qt.hsva(1 - root.visualPosition, 1, 1, 1);
} else {
// Linear Interpolation for Standard Gradients
// visualPosition 0.0 = Top, 1.0 = Bottom
var t = root.visualPosition;
var r = root.topColor.r * (1 - t) + root.bottomColor.r * t;
var g = root.topColor.g * (1 - t) + root.bottomColor.g * t;
var b = root.topColor.b * (1 - t) + root.bottomColor.b * t;
return Qt.rgba(r, g, b, 1);
}
}
border.color: root.pressed ? Color.mHover : Color.mPrimary
border.width: Style.borderL
anchors.centerIn: parent
Behavior on color {
ColorAnimation {
duration: Style.animationFast
}
}
}
MouseArea {
enabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
acceptedButtons: Qt.NoButton // Don't accept any mouse buttons - only hover
propagateComposedEvents: true
onEntered: {
root.hovering = true;
if (root.tooltipText) {
TooltipService.show(knob, root.tooltipText, root.tooltipDirection);
}
}
onExited: {
root.hovering = false;
if (root.tooltipText) {
TooltipService.hide();
}
}
}
// Hide tooltip when slider is pressed (anywhere on the slider)
Connections {
target: root
function onPressedChanged() {
if (root.pressed && root.tooltipText) {
TooltipService.hide();
}
}
}
}
layer.enabled: true
}