Files
noctalia-shell/Widgets/NQuickSetting.qml
T
2025-10-09 15:17:23 +02:00

398 lines
9.5 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects
import qs.Commons
import qs.Services
Rectangle {
id: root
// Public properties
property string text: ""
property string icon: ""
property string tooltipText: ""
property bool enabled: true
property bool active: false
property bool compact: false
property string style: "modern" // "modern", "classic", or "compact"
// Styling properties
property real fontSize: Style.fontSizeS * scaling
property int fontWeight: Style.fontWeightMedium
property real iconSize: Style.fontSizeL * scaling
property real cornerRadius: Style.radiusM * scaling
// Colors - Style-dependent colors
property color backgroundColor: {
if (style === "classic")
return Color.mSurfaceVariant
if (style === "compact")
return Color.mSurface
return Color.mSurface
}
property color textColor: Color.mOnSurface
property color iconColor: {
if (style === "classic")
return Color.mPrimary
if (style === "compact")
return active ? Color.mPrimary : Color.mOnSurface
return active ? Color.mPrimary : Color.mOnSurface
}
property color borderColor: Color.mOutline
property color hoverColor: {
if (style === "classic")
return Color.mTertiary
if (style === "compact")
return Color.mPrimary
return Color.mPrimary
}
property color pressedColor: {
if (style === "classic")
return Color.mTertiary
if (style === "compact")
return Qt.darker(Color.mPrimary, 1.1)
return Qt.darker(Color.mPrimary, 1.1)
}
property color hoverTextColor: Color.mOnPrimary
property color hoverIconColor: {
if (style === "classic")
return Color.mOnTertiary
if (style === "compact")
return Color.mOnPrimary
return Color.mOnPrimary
}
// Signals
signal clicked
signal rightClicked
signal middleClicked
// Internal properties
property bool hovered: false
property bool pressed: false
property real scaling: 1.0
// Dimensions - Style-dependent sizing
implicitWidth: {
if (style === "classic") {
return Style.baseWidgetSize * scaling
}
if (style === "compact") {
return Style.baseWidgetSize * 0.8 * scaling
}
return compact ? Math.max(100 * scaling, contentRow.implicitWidth + (Style.marginL * scaling)) : Math.max(120 * scaling, contentRow.implicitWidth + (Style.marginL * scaling))
}
implicitHeight: {
if (style === "classic") {
return Style.baseWidgetSize * scaling
}
if (style === "compact") {
return Style.baseWidgetSize * 0.8 * scaling
}
return compact ? Math.max(48 * scaling, contentRow.implicitHeight + (Style.marginM * scaling)) : Math.max(56 * scaling, contentRow.implicitHeight + (Style.marginL * scaling))
}
// Appearance - Style-dependent styling
radius: {
if (style === "classic")
return width * 0.5
if (style === "compact")
return Style.radiusS * scaling // Smaller radius for compact
return cornerRadius
}
color: {
if (!enabled)
return Qt.lighter(Color.mSurface, 1.1)
if (pressed)
return pressedColor
if (hovered)
return hoverColor
return backgroundColor
}
border.width: {
if (style === "classic")
return Math.max(1, Style.borderS * scaling)
if (style === "compact")
return 0
return 0
}
border.color: {
if (style === "classic")
return borderColor
return "transparent"
}
opacity: enabled ? (style === "classic" ? Style.opacityFull : 1.0) : (style === "classic" ? Style.opacityMedium : 0.6)
Behavior on color {
ColorAnimation {
duration: style === "classic" ? Style.animationNormal : Style.animationFast
easing.type: style === "classic" ? Easing.InOutQuad : Easing.OutCubic
}
}
Behavior on border.color {
ColorAnimation {
duration: style === "classic" ? Style.animationNormal : Style.animationFast
easing.type: style === "classic" ? Easing.InOutQuad : Easing.OutCubic
}
}
Behavior on scale {
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
// Hover scale effect
scale: hovered ? 1.02 : 1.0
// Subtle shadow/elevation effect
Rectangle {
anchors.fill: parent
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.1)
visible: active
z: -1
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
// Modern style - icon above text
ColumnLayout {
id: contentRow
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
visible: root.style !== "classic" && root.style !== "compact"
// Icon
NIcon {
Layout.alignment: Qt.AlignHCenter
visible: root.icon !== ""
icon: root.icon
pointSize: root.iconSize
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
if (root.hovered)
return root.hoverIconColor
return root.iconColor
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
// Text content
NText {
Layout.alignment: Qt.AlignHCenter
visible: root.text !== "" && !compact
text: root.text
pointSize: root.fontSize
font.weight: root.fontWeight
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
if (root.hovered)
return root.hoverTextColor
return root.textColor
}
elide: Text.ElideRight
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
}
// Compact style - icon only, small square button
NIcon {
id: compactIcon
anchors.centerIn: parent
visible: root.style === "compact" && root.icon !== ""
icon: root.icon
pointSize: Style.fontSizeM * scaling // Smaller icon for compact
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
if (root.hovered)
return root.hoverIconColor
return root.iconColor
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
// Classic style - EXACTLY like NIconButton (icon + text)
RowLayout {
anchors.centerIn: parent
visible: root.style === "classic"
spacing: Style.marginXS * scaling
NIcon {
visible: root.icon !== ""
icon: root.icon
pointSize: Style.fontSizeM * scaling
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
if (root.hovered)
return root.hoverIconColor
return root.iconColor
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
NText {
visible: root.text !== ""
text: root.text
pointSize: root.fontSize
font.weight: root.fontWeight
color: {
if (!root.enabled)
return Color.mOnSurfaceVariant
if (root.hovered)
return root.hoverTextColor
return root.textColor
}
Behavior on color {
ColorAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
enabled: root.enabled
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onEntered: {
root.hovered = true
if (tooltipText) {
TooltipService.show(Screen, root, root.tooltipText)
}
}
onExited: {
root.hovered = false
if (tooltipText) {
TooltipService.hide()
}
}
onPressed: mouse => {
root.pressed = true
root.scale = 0.95
if (tooltipText) {
TooltipService.hide()
}
}
onReleased: mouse => {
root.pressed = false
root.scale = 1.0
if (mouse.button === Qt.LeftButton) {
root.clicked()
} else if (mouse.button === Qt.RightButton) {
root.rightClicked()
} else if (mouse.button === Qt.MiddleButton) {
root.middleClicked()
}
}
onCanceled: {
root.hovered = false
root.pressed = false
root.scale = 1.0
if (tooltipText) {
TooltipService.hide()
}
}
}
Rectangle {
id: ripple
anchors.fill: parent
radius: parent.radius
color: Qt.rgba(1, 1, 1, 0.2)
scale: 0
opacity: 0
visible: false
SequentialAnimation {
id: rippleAnimation
running: false
ParallelAnimation {
NumberAnimation {
target: ripple
property: "scale"
from: 0
to: 1.2
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
NumberAnimation {
target: ripple
property: "opacity"
from: 0.6
to: 0
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
}
}
Connections {
target: root
function onClicked() {
ripple.visible = true
rippleAnimation.start()
}
}
Connections {
target: rippleAnimation
function onFinished() {
ripple.visible = false
ripple.scale = 0
ripple.opacity = 0
}
}
}