diff --git a/Components/CollapsibleSection.qml b/Components/CollapsibleSection.qml deleted file mode 100644 index 46e4753..0000000 --- a/Components/CollapsibleSection.qml +++ /dev/null @@ -1,135 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Config - -ColumnLayout { - id: root - - default property alias content: contentColumn.data - property string description: "" - property bool expanded: false - property bool nested: false - property bool showBackground: false - required property string title - - signal toggleRequested - - Layout.fillWidth: true - spacing: Appearance.spacing.small - - Item { - id: sectionHeaderItem - - Layout.fillWidth: true - Layout.preferredHeight: Math.max(titleRow.implicitHeight + Appearance.padding.normal * 2, 48) - - RowLayout { - id: titleRow - - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.normal - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.normal - anchors.verticalCenter: parent.verticalCenter - spacing: Appearance.spacing.normal - - CustomText { - font.pointSize: Appearance.font.size.larger - font.weight: 500 - text: root.title - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - color: DynamicColors.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.normal - rotation: root.expanded ? 180 : 0 - text: "expand_more" - - Behavior on rotation { - Anim { - duration: Appearance.anim.durations.small - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - } - - StateLayer { - anchors.fill: parent - color: DynamicColors.palette.m3onSurface - radius: Appearance.rounding.normal - showHoverBackground: false - - onClicked: { - root.toggleRequested(); - root.expanded = !root.expanded; - } - } - } - - Item { - id: contentWrapper - - Layout.fillWidth: true - Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Appearance.spacing.small * 2) : 0 - clip: true - - Behavior on Layout.preferredHeight { - Anim { - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - CustomRect { - id: backgroundRect - - anchors.fill: parent - color: DynamicColors.transparency.enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, root.nested ? 3 : 2) : (root.nested ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3surfaceContainer) - opacity: root.showBackground && root.expanded ? 1.0 : 0.0 - radius: Appearance.rounding.normal - visible: root.showBackground - - Behavior on opacity { - Anim { - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - - ColumnLayout { - id: contentColumn - - anchors.bottomMargin: Appearance.spacing.small - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.normal - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.normal - opacity: root.expanded ? 1.0 : 0.0 - spacing: Appearance.spacing.small - y: Appearance.spacing.small - - Behavior on opacity { - Anim { - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - CustomText { - id: descriptionText - - Layout.bottomMargin: root.description !== "" ? Appearance.spacing.small : 0 - Layout.fillWidth: true - Layout.topMargin: root.description !== "" ? Appearance.spacing.smaller : 0 - color: DynamicColors.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.small - text: root.description - visible: root.description !== "" - wrapMode: Text.Wrap - } - } - } -} diff --git a/Components/CustomAudioSlider.qml b/Components/CustomAudioSlider.qml deleted file mode 100644 index f896d83..0000000 --- a/Components/CustomAudioSlider.qml +++ /dev/null @@ -1,70 +0,0 @@ -import QtQuick -import QtQuick.Templates -import qs.Config - -Slider { - id: root - - property color nonPeakColor: DynamicColors.tPalette.m3primary - required property real peak - property color peakColor: DynamicColors.palette.m3primary - - background: Item { - CustomRect { - anchors.bottom: parent.bottom - anchors.bottomMargin: root.implicitHeight / 3 - anchors.left: parent.left - anchors.top: parent.top - anchors.topMargin: root.implicitHeight / 3 - bottomRightRadius: root.implicitHeight / 15 - color: root.nonPeakColor - implicitWidth: root.handle.x - root.implicitHeight - radius: Appearance.rounding.full - topRightRadius: root.implicitHeight / 15 - - CustomRect { - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.top: parent.top - bottomRightRadius: root.implicitHeight / 15 - color: root.peakColor - implicitWidth: parent.width * root.peak - radius: Appearance.rounding.full - topRightRadius: root.implicitHeight / 15 - - Behavior on implicitWidth { - Anim { - duration: 50 - } - } - } - } - - CustomRect { - anchors.bottom: parent.bottom - anchors.bottomMargin: root.implicitHeight / 3 - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: root.implicitHeight / 3 - bottomLeftRadius: root.implicitHeight / 15 - color: DynamicColors.tPalette.m3surfaceContainer - implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight - radius: Appearance.rounding.full - topLeftRadius: root.implicitHeight / 15 - } - } - handle: CustomRect { - anchors.verticalCenter: parent.verticalCenter - color: DynamicColors.palette.m3primary - implicitHeight: 15 - implicitWidth: 5 - radius: Appearance.rounding.full - x: root.visualPosition * root.availableWidth - implicitWidth / 2 - - MouseArea { - acceptedButtons: Qt.NoButton - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - } - } -} diff --git a/Components/CustomBusyIndicator.qml b/Components/CustomBusyIndicator.qml deleted file mode 100644 index 877db30..0000000 --- a/Components/CustomBusyIndicator.qml +++ /dev/null @@ -1,69 +0,0 @@ -import QtQuick -import QtQuick.Controls.Basic - -BusyIndicator { - id: control - - property int busySize: 64 - property color color: delegate.color - - contentItem: Item { - implicitHeight: control.busySize - implicitWidth: control.busySize - - Item { - id: item - - height: control.busySize - opacity: control.running ? 1 : 0 - width: control.busySize - x: parent.width / 2 - (control.busySize / 2) - y: parent.height / 2 - (control.busySize / 2) - - Behavior on opacity { - OpacityAnimator { - duration: 250 - } - } - - RotationAnimator { - duration: 1250 - from: 0 - loops: Animation.Infinite - running: control.visible && control.running - target: item - to: 360 - } - - Repeater { - id: repeater - - model: 6 - - CustomRect { - id: delegate - - required property int index - - color: control.color - implicitHeight: 10 - implicitWidth: 10 - radius: 5 - x: item.width / 2 - width / 2 - y: item.height / 2 - height / 2 - - transform: [ - Translate { - y: -Math.min(item.width, item.height) * 0.5 + 5 - }, - Rotation { - angle: delegate.index / repeater.count * 360 - origin.x: 5 - origin.y: 5 - } - ] - } - } - } - } -} diff --git a/Components/CustomButton.qml b/Components/CustomButton.qml deleted file mode 100644 index b98710f..0000000 --- a/Components/CustomButton.qml +++ /dev/null @@ -1,32 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Config - -Button { - id: control - - property color bgColor: DynamicColors.palette.m3primary - property int radius: Appearance.rounding.smallest / 2 - property color textColor: DynamicColors.palette.m3onPrimary - - background: CustomRect { - color: control.bgColor - opacity: control.enabled ? 1.0 : 0.5 - radius: control.radius - } - contentItem: CustomText { - color: control.textColor - horizontalAlignment: Text.AlignHCenter - opacity: control.enabled ? 1.0 : 0.5 - text: control.text - verticalAlignment: Text.AlignVCenter - } - - StateLayer { - radius: control.radius - - onClicked: { - control.clicked(); - } - } -} diff --git a/Components/CustomComboBox.qml b/Components/CustomComboBox.qml deleted file mode 100644 index 2a0186f..0000000 --- a/Components/CustomComboBox.qml +++ /dev/null @@ -1,169 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import qs.Config - -ComboBox { - id: root - - property int cornerRadius: Appearance.rounding.normal - property int fieldHeight: 42 - property bool filled: true - property real focusRingOpacity: 0.70 - property int hPadding: 16 - property int menuCornerRadius: 16 - property int menuRowHeight: 46 - property int menuVisibleRows: 7 - property bool preferPopupWindow: false - - hoverEnabled: true - implicitHeight: fieldHeight - implicitWidth: 240 - spacing: 8 - - // ---------- Field background (filled/outlined + state layers + focus ring) ---------- - background: Item { - anchors.fill: parent - - CustomRect { - id: container - - anchors.fill: parent - color: DynamicColors.palette.m3surfaceVariant - radius: root.cornerRadius - - StateLayer { - } - } - } - - // ---------- Content ---------- - contentItem: RowLayout { - anchors.fill: parent - anchors.leftMargin: root.hPadding - anchors.rightMargin: root.hPadding - spacing: 12 - - // Display text - CustomText { - Layout.fillWidth: true - color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant - elide: Text.ElideRight - font.pixelSize: 16 - font.weight: Font.Medium - text: root.currentText - verticalAlignment: Text.AlignVCenter - } - - // Indicator chevron (simple, replace with your icon system) - CustomText { - color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurfaceVariant - rotation: root.popup.visible ? 180 : 0 - text: "▾" - transformOrigin: Item.Center - verticalAlignment: Text.AlignVCenter - - Behavior on rotation { - NumberAnimation { - duration: 140 - easing.type: Easing.OutCubic - } - } - } - } - popup: Popup { - id: p - - implicitHeight: list.contentItem.height + Appearance.padding.small * 2 - implicitWidth: root.width - modal: true - popupType: root.preferPopupWindow ? Popup.Window : Popup.Item - y: -list.currentIndex * (root.menuRowHeight + Appearance.spacing.small) - Appearance.padding.small - - background: CustomRect { - color: DynamicColors.palette.m3surface - radius: root.menuCornerRadius - } - contentItem: ListView { - id: list - - anchors.bottomMargin: Appearance.padding.small - anchors.fill: parent - anchors.topMargin: Appearance.padding.small - clip: true - currentIndex: root.currentIndex - model: root.delegateModel - spacing: Appearance.spacing.small - - delegate: CustomRect { - required property int index - required property var modelData - - anchors.horizontalCenter: parent.horizontalCenter - color: (index === root.currentIndex) ? DynamicColors.palette.m3primary : "transparent" - implicitHeight: root.menuRowHeight - implicitWidth: p.implicitWidth - Appearance.padding.small * 2 - radius: Appearance.rounding.normal - Appearance.padding.small - - RowLayout { - anchors.fill: parent - spacing: 10 - - CustomText { - Layout.fillWidth: true - color: DynamicColors.palette.m3onSurface - elide: Text.ElideRight - font.pixelSize: 15 - text: modelData - verticalAlignment: Text.AlignVCenter - } - - CustomText { - color: DynamicColors.palette.m3onSurfaceVariant - text: "✓" - verticalAlignment: Text.AlignVCenter - visible: index === root.currentIndex - } - } - - StateLayer { - onClicked: { - root.currentIndex = index; - p.close(); - } - } - } - } - - // Expressive-ish open/close motion: subtle scale+fade (tune to taste). :contentReference[oaicite:5]{index=5} - enter: Transition { - Anim { - from: 0 - property: "opacity" - to: 1 - } - - Anim { - from: 0.98 - property: "scale" - to: 1.0 - } - } - exit: Transition { - Anim { - from: 1 - property: "opacity" - to: 0 - } - } - - Elevation { - anchors.fill: parent - level: 2 - radius: root.menuCornerRadius - z: -1 - } - } -} diff --git a/Components/CustomTooltip.qml b/Components/CustomTooltip.qml deleted file mode 100644 index 59cb1ad..0000000 --- a/Components/CustomTooltip.qml +++ /dev/null @@ -1,25 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Components - -ToolTip { - id: root - - property bool alternativeVisibleCondition: false - property bool extraVisibleCondition: true - readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition - - background: null - horizontalPadding: 10 - verticalPadding: 5 - visible: internalVisibleCondition - - contentItem: CustomTooltipContent { - id: contentItem - - horizontalPadding: root.horizontalPadding - shown: root.internalVisibleCondition - text: root.text - verticalPadding: root.verticalPadding - } -} diff --git a/Components/CustomTooltipContent.qml b/Components/CustomTooltipContent.qml deleted file mode 100644 index 00a4270..0000000 --- a/Components/CustomTooltipContent.qml +++ /dev/null @@ -1,54 +0,0 @@ -import QtQuick -import qs.Components -import qs.Config - -Item { - id: root - - property real horizontalPadding: 10 - property bool isVisible: backgroundRectangle.implicitHeight > 0 - property bool shown: false - required property string text - property real verticalPadding: 5 - - implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding - implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding - - Rectangle { - id: backgroundRectangle - - clip: true - color: DynamicColors.tPalette.m3inverseSurface ?? "#3C4043" - implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0 - implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0 - opacity: shown ? 1 : 0 - radius: Appearance.rounding.smallest - - Behavior on implicitHeight { - Anim { - } - } - Behavior on implicitWidth { - Anim { - } - } - Behavior on opacity { - Anim { - } - } - - anchors { - bottom: root.bottom - horizontalCenter: root.horizontalCenter - } - - CustomText { - id: tooltipTextObject - - anchors.centerIn: parent - color: DynamicColors.palette.m3inverseOnSurface ?? "#FFFFFF" - text: root.text - wrapMode: Text.Wrap - } - } -} diff --git a/Components/ExtraIndicator.qml b/Components/ExtraIndicator.qml deleted file mode 100644 index 34315e7..0000000 --- a/Components/ExtraIndicator.qml +++ /dev/null @@ -1,44 +0,0 @@ -import qs.Config -import QtQuick - -CustomRect { - required property int extra - - anchors.margins: 8 - anchors.right: parent.right - color: DynamicColors.palette.m3tertiary - implicitHeight: count.implicitHeight + 4 * 2 - implicitWidth: count.implicitWidth + 8 * 2 - opacity: extra > 0 ? 1 : 0 - radius: Appearance.rounding.smallest - scale: extra > 0 ? 1 : 0.5 - - Behavior on opacity { - Anim { - duration: MaterialEasing.expressiveEffectsTime - } - } - Behavior on scale { - Anim { - duration: MaterialEasing.expressiveEffectsTime - easing.bezierCurve: MaterialEasing.expressiveEffects - } - } - - Elevation { - anchors.fill: parent - level: 2 - opacity: parent.opacity - radius: parent.radius - z: -1 - } - - CustomText { - id: count - - anchors.centerIn: parent - animate: parent.opacity > 0 - color: DynamicColors.palette.m3onTertiary - text: qsTr("+%1").arg(parent.extra) - } -} diff --git a/Components/HoverIconButton.qml b/Components/HoverIconButton.qml deleted file mode 100644 index cd17ad0..0000000 --- a/Components/HoverIconButton.qml +++ /dev/null @@ -1,33 +0,0 @@ -import QtQuick -import QtQuick.Controls -import qs.Config - -IconButton { - id: root - - required property bool shouldBeVisible - - opacity: 0 - scale: 0 - visible: root.scale > 0 - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.small - } - } - Behavior on scale { - Anim { - } - } - - onShouldBeVisibleChanged: { - if (root.shouldBeVisible) { - root.opacity = 1; - root.scale = 1; - } else { - root.opacity = 0; - root.scale = 0; - } - } -} diff --git a/Components/M3TextField.qml b/Components/M3TextField.qml deleted file mode 100644 index 8f23325..0000000 --- a/Components/M3TextField.qml +++ /dev/null @@ -1,309 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import qs.Config - -Item { - id: root - - enum Variant { - Outlined, - Filled - } - - readonly property color accent: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline)) - readonly property real disabledOpacity: 0.38 - readonly property string effectiveTrailingIcon: root.trailingIcon.length > 0 ? root.trailingIcon : (root.password ? (field.echoMode === TextInput.Password ? "visibility" : "visibility_off") : (root.isError ? "error" : "")) - property string errorText: "" - readonly property alias field: field - readonly property bool floating: focused || hasContent || prefixText.length > 0 - readonly property bool focused: field.activeFocus - readonly property bool hasContent: field.text.length > 0 - readonly property color iconColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant)) - property int inputMethodHints: Qt.ImhNone - property bool isError: false - property string label: "" - readonly property color labelColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant)) - property string leadingIcon: "" - property int maxLength: 0 - property color notchColor: DynamicColors.palette.m3surface - readonly property bool outlined: variant === M3TextField.Variant.Outlined - property bool password: false - property string placeholder: "" - property string prefixText: "" - property string suffixText: "" - readonly property color supportColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant) - property string supportingText: "" - property alias text: field.text - property string trailingIcon: "" - readonly property bool trailingInteractive: root.password && root.trailingIcon.length === 0 - property var validator: null - property int variant: M3TextField.Variant.Outlined - - signal accepted - - function forceFieldFocus(): void { - field.forceActiveFocus(); - } - - implicitHeight: 56 + (supportRow.visible ? supportRow.implicitHeight + Appearance.spacing.extraSmall : 0) - implicitWidth: 240 - - CustomRect { - id: filledBg - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - color: root.enabled ? (root.focused ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) : DynamicColors.palette.m3surfaceContainerHigh) : DynamicColors.palette.m3onSurface - implicitHeight: 56 - opacity: root.enabled ? 1 : 0.04 // M3 filled disabled container @ 4% - - topLeftRadius: Appearance.rounding.small - topRightRadius: Appearance.rounding.small - visible: !root.outlined - - Behavior on color { - CAnim { - } - } - - StateLayer { - anchors.fill: parent - color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface - enabled: root.enabled - topLeftRadius: Appearance.rounding.small - topRightRadius: Appearance.rounding.small - - onClicked: field.forceActiveFocus() - } - - CustomRect { - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - color: root.accent - implicitHeight: root.focused || root.isError ? 2 : 1 - opacity: root.enabled ? 1 : root.disabledOpacity - - Behavior on color { - CAnim { - } - } - Behavior on implicitHeight { - Anim { - type: Anim.StandardSmall - } - } - } - } - - CustomRect { - id: outline - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - border.color: root.accent - border.width: root.focused || root.isError ? 2 : 1 - color: "transparent" - implicitHeight: 56 - opacity: root.enabled ? 1 : root.disabledOpacity - radius: Appearance.rounding.small - visible: root.outlined - - Behavior on border.color { - CAnim { - } - } - Behavior on border.width { - Anim { - type: Anim.StandardSmall - } - } - - StateLayer { - anchors.fill: parent - anchors.margins: outline.border.width - color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface - enabled: root.enabled - radius: Appearance.rounding.small - - onClicked: field.forceActiveFocus() - } - } - - Rectangle { - color: root.notchColor - height: outline.border.width - opacity: root.enabled ? 1 : root.disabledOpacity - visible: root.outlined && root.floating && root.label.length > 0 - width: labelText.width + Appearance.spacing.extraSmall - x: labelText.x - Appearance.spacing.extraSmall / 2 - y: 0 - } - - CustomText { - id: labelText - - color: root.labelColor - font.pointSize: root.floating ? Appearance.font.size.small : Appearance.font.size.medium - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.label - visible: root.label.length > 0 - x: root.floating ? (Appearance.padding.larger + Appearance.spacing.extraSmall / 2) : (leading.visible ? leading.x + leading.width + Appearance.spacing.small : Appearance.padding.larger) - y: root.floating ? (root.outlined ? -height / 2 : Appearance.padding.extraSmall) : (56 - height) / 2 - - Behavior on color { - CAnim { - } - } - Behavior on x { - Anim { - type: Anim.StandardSmall - } - } - Behavior on y { - Anim { - type: Anim.StandardSmall - } - } - } - - MaterialIcon { - id: leading - - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.larger - anchors.top: parent.top - anchors.topMargin: (56 - height) / 2 - color: root.iconColor - font.pointSize: Appearance.font.size.medium - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.leadingIcon - visible: root.leadingIcon.length > 0 - - Behavior on color { - CAnim { - } - } - } - - MaterialIcon { - id: trailing - - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.larger - anchors.top: parent.top - anchors.topMargin: (56 - height) / 2 - color: root.isError ? DynamicColors.palette.m3error : (root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface) - font.pointSize: Appearance.font.size.medium - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.effectiveTrailingIcon - visible: root.effectiveTrailingIcon.length > 0 - - MouseArea { - anchors.fill: parent - anchors.margins: -Appearance.padding.small - cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor - enabled: root.trailingInteractive && root.enabled - - onClicked: field.echoMode = field.echoMode === TextInput.Password ? TextInput.Normal : TextInput.Password - } - } - - CustomText { - id: prefix - - anchors.left: leading.visible ? leading.right : parent.left - anchors.leftMargin: leading.visible ? Appearance.spacing.small : Appearance.padding.larger - anchors.top: parent.top - anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0 - color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface - font.pointSize: Appearance.font.size.medium - height: 56 - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.prefixText - verticalAlignment: Text.AlignVCenter - visible: root.prefixText.length > 0 && root.floating - } - - CustomText { - id: suffix - - anchors.right: trailing.visible ? trailing.left : parent.right - anchors.rightMargin: trailing.visible ? Appearance.spacing.small : Appearance.padding.larger - anchors.top: parent.top - anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0 - color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface - font.pointSize: Appearance.font.size.medium - height: 56 - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.suffixText - verticalAlignment: Text.AlignVCenter - visible: root.suffixText.length > 0 && root.floating - } - - CustomTextField { - id: field - - anchors.left: prefix.visible ? prefix.right : (leading.visible ? leading.right : parent.left) - anchors.leftMargin: prefix.visible ? Appearance.spacing.extraSmall : (leading.visible ? Appearance.spacing.small : Appearance.padding.larger) - anchors.right: suffix.visible ? suffix.left : (trailing.visible ? trailing.left : parent.right) - anchors.rightMargin: suffix.visible ? Appearance.spacing.extraSmall : (trailing.visible ? Appearance.spacing.small : Appearance.padding.larger) - anchors.top: parent.top - color: DynamicColors.palette.m3onSurface - echoMode: root.password ? TextInput.Password : TextInput.Normal - enabled: root.enabled - font.pointSize: Appearance.font.size.medium - height: 56 - inputMethodHints: root.inputMethodHints - maximumLength: root.maxLength > 0 ? root.maxLength : 32767 - opacity: root.enabled ? 1 : root.disabledOpacity - placeholderText: root.focused ? root.placeholder : "" - topPadding: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0 - validator: root.validator - verticalAlignment: TextInput.AlignVCenter - - onAccepted: root.accepted() - onTextEdited: if (root.isError) - root.isError = false - } - - Item { - id: supportRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: 56 + Appearance.spacing.extraSmall - implicitHeight: Math.max(supportText.implicitHeight, counter.implicitHeight) - visible: (root.isError && root.errorText.length > 0) || root.supportingText.length > 0 || root.maxLength > 0 - - CustomText { - id: supportText - - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.larger - anchors.right: counter.visible ? counter.left : parent.right - anchors.rightMargin: Appearance.spacing.small - color: root.supportColor - font.pointSize: Appearance.font.size.small - opacity: root.enabled ? 1 : root.disabledOpacity - text: root.isError && root.errorText.length > 0 ? root.errorText : root.supportingText - wrapMode: Text.WordWrap - } - - CustomText { - id: counter - - anchors.right: parent.right - anchors.rightMargin: Appearance.padding.larger - color: root.supportColor - font.pointSize: Appearance.font.size.small - opacity: root.enabled ? 1 : root.disabledOpacity - text: `${root.text.length}/${root.maxLength}` - visible: root.maxLength > 0 - } - } -} diff --git a/Components/PathMenu.qml b/Components/PathMenu.qml deleted file mode 100644 index 0e2d10d..0000000 --- a/Components/PathMenu.qml +++ /dev/null @@ -1,76 +0,0 @@ -import QtQuick - -Path { - id: root - - required property real viewHeight - required property real viewWidth - - startX: root.viewWidth / 2 - startY: 0 - - PathAttribute { - name: "itemOpacity" - value: 0.25 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight * (1 / 6) - } - - PathAttribute { - name: "itemOpacity" - value: 0.45 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight * (2 / 6) - } - - PathAttribute { - name: "itemOpacity" - value: 0.70 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight * (3 / 6) - } - - PathAttribute { - name: "itemOpacity" - value: 1.00 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight * (4 / 6) - } - - PathAttribute { - name: "itemOpacity" - value: 0.70 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight * (5 / 6) - } - - PathAttribute { - name: "itemOpacity" - value: 0.45 - } - - PathLine { - x: root.viewWidth / 2 - y: root.viewHeight - } - - PathAttribute { - name: "itemOpacity" - value: 0.25 - } -} diff --git a/Components/PathViewMenu.qml b/Components/PathViewMenu.qml deleted file mode 100644 index 111b236..0000000 --- a/Components/PathViewMenu.qml +++ /dev/null @@ -1,180 +0,0 @@ -import QtQuick -import QtQuick.Effects -import qs.Config - -Elevation { - id: root - - required property int currentIndex - property bool expanded - required property int from - property color insideTextColor: DynamicColors.palette.m3onPrimary - property int itemHeight - property int listHeight: 200 - property color outsideTextColor: DynamicColors.palette.m3onSurfaceVariant - readonly property var spinnerModel: root.range(root.from, root.to) - required property int to - property Item triggerItem - - signal itemSelected(item: int) - - function range(first, last) { - let out = []; - for (let i = first; i <= last; ++i) - out.push(i); - return out; - } - - implicitHeight: root.expanded ? view.implicitHeight : 0 - level: root.expanded ? 2 : 0 - radius: itemHeight / 2 - visible: implicitHeight > 0 - z: root.expanded ? 100 : 0 - - Behavior on implicitHeight { - Anim { - } - } - - onExpandedChanged: { - if (!root.expanded) - root.itemSelected(view.currentIndex + 1); - } - - Component { - id: spinnerDelegate - - Item { - id: wrapper - - readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white" - required property var modelData - - height: root.itemHeight - opacity: wrapper.PathView.itemOpacity - visible: wrapper.PathView.onPath - width: wrapper.PathView.view ? wrapper.PathView.view.width : 0 - z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100) - - CustomText { - anchors.centerIn: parent - color: wrapper.delegateTextColor - font.pointSize: Appearance.font.size.large - text: wrapper.modelData - } - } - } - - CustomClippingRect { - anchors.fill: parent - color: DynamicColors.palette.m3surfaceContainer - radius: parent.radius - z: root.z - - // Main visible spinner: normal/outside text color - PathView { - id: view - - property color delegateTextColor: root.outsideTextColor - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - clip: true - currentIndex: root.currentIndex - 1 - delegate: spinnerDelegate - dragMargin: width - highlightRangeMode: PathView.StrictlyEnforceRange - implicitHeight: root.listHeight - model: root.spinnerModel - pathItemCount: 7 - preferredHighlightBegin: 0.5 - preferredHighlightEnd: 0.5 - snapMode: PathView.SnapToItem - - path: PathMenu { - viewHeight: view.height - viewWidth: view.width - } - } - - // The selection rectangle itself - CustomRect { - id: selectionRect - - anchors.verticalCenter: parent.verticalCenter - color: DynamicColors.palette.m3primary - height: root.itemHeight - radius: root.itemHeight / 2 - width: parent.width - z: 2 - } - - // Hidden source: same PathView, but with the "inside selection" text color - Item { - id: selectedTextSource - - anchors.fill: parent - layer.enabled: true - visible: false - - PathView { - id: selectedTextView - - property color delegateTextColor: root.insideTextColor - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - clip: true - currentIndex: view.currentIndex - delegate: spinnerDelegate - dragMargin: view.dragMargin - highlightRangeMode: view.highlightRangeMode - implicitHeight: root.listHeight - interactive: false - model: view.model - - // Keep this PathView visually locked to the real one - offset: view.offset - pathItemCount: view.pathItemCount - preferredHighlightBegin: view.preferredHighlightBegin - preferredHighlightEnd: view.preferredHighlightEnd - snapMode: view.snapMode - - path: PathMenu { - viewHeight: selectedTextView.height - viewWidth: selectedTextView.width - } - } - } - - // Mask matching the selection rectangle - Item { - id: selectionMask - - anchors.fill: parent - layer.enabled: true - visible: false - - CustomRect { - color: "white" - height: selectionRect.height - radius: selectionRect.radius - width: selectionRect.width - x: selectionRect.x - y: selectionRect.y - } - } - - // Only show the "inside selection" text where the mask exists - MultiEffect { - anchors.fill: selectedTextSource - maskEnabled: true - maskInverted: false - maskSource: selectionMask - source: selectedTextSource - z: 3 - } - } -} diff --git a/Components/SpinBoxRow.qml b/Components/SpinBoxRow.qml deleted file mode 100644 index 6686ec7..0000000 --- a/Components/SpinBoxRow.qml +++ /dev/null @@ -1,50 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Config - -CustomRect { - id: root - - required property string label - required property real max - required property real min - property var onValueModified: function (value) {} - property real step: 1 - required property real value - - Layout.fillWidth: true - color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2) - implicitHeight: row.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - - Behavior on implicitHeight { - Anim { - } - } - - RowLayout { - id: row - - anchors.left: parent.left - anchors.margins: Appearance.padding.large - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - spacing: Appearance.spacing.normal - - CustomText { - Layout.fillWidth: true - text: root.label - } - - CustomSpinBox { - max: root.max - min: root.min - step: root.step - value: root.value - - onValueModified: value => { - root.onValueModified(value); - } - } - } -} diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml index d485d7c..e0b6ac0 100644 --- a/Config/BarConfig.qml +++ b/Config/BarConfig.qml @@ -68,10 +68,12 @@ JsonObject { property bool upower: true } component Tray: JsonObject { + property bool recolorIcons: false property bool showAudio: true property bool showBluetooth: false property bool showMicrophone: true property bool showNetwork: false + property bool showOnHover: false property bool showPower: true property bool showWifi: false property int trayIconSize: 24 diff --git a/Drawers/Interactions.qml b/Drawers/Interactions.qml index 363ab4e..3fd7ee2 100644 --- a/Drawers/Interactions.qml +++ b/Drawers/Interactions.qml @@ -170,7 +170,7 @@ Item { root.panels.osd.hovered = false; } - if (!root.popouts.currentName.startsWith("traymenu")) { + if (!root.popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) { root.popouts.hasCurrent = false; } diff --git a/Drawers/Windows.qml b/Drawers/Windows.qml index 6d6842e..2be2e04 100644 --- a/Drawers/Windows.qml +++ b/Drawers/Windows.qml @@ -127,7 +127,7 @@ CustomWindow { HyprlandFocusGrab { id: focusGrab - active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu")) + active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && !Config.bar.tray.showOnHover) windows: [root] onCleared: { diff --git a/Greeter/Content.qml b/Greeter/Content.qml index 6ebf790..f2f314a 100644 --- a/Greeter/Content.qml +++ b/Greeter/Content.qml @@ -30,18 +30,6 @@ RowLayout { } } - // CustomRect { - // Layout.fillWidth: true - // color: DynamicColors.tPalette.m3surfaceContainer - // implicitHeight: resources.implicitHeight - // radius: Appearance.rounding.small - // - // Resources { - // id: resources - // - // } - // } - CustomClippingRect { Layout.fillHeight: true Layout.fillWidth: true diff --git a/Greeter/Helpers/GetIcons.qml b/Greeter/Helpers/GetIcons.qml deleted file mode 100644 index e957ad3..0000000 --- a/Greeter/Helpers/GetIcons.qml +++ /dev/null @@ -1,17 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - id: root - - function getTrayIcon(id: string, icon: string): string { - if (icon.includes("?path=")) { - const [name, path] = icon.split("?path="); - icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`); - } else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") { - icon = icon.replace("qspixmap", "icon/discord-tray"); - } - return icon; - } -} diff --git a/Greeter/Helpers/SettingsDropdowns.qml b/Greeter/Helpers/SettingsDropdowns.qml deleted file mode 100644 index 4a4ac27..0000000 --- a/Greeter/Helpers/SettingsDropdowns.qml +++ /dev/null @@ -1,60 +0,0 @@ -pragma Singleton -import QtQuick - -QtObject { - id: root - - property Item activeMenu: null - property Item activeTrigger: null - - function close(menu) { - if (!menu) - return; - - if (activeMenu === menu) { - activeMenu = null; - activeTrigger = null; - } - - menu.expanded = false; - } - - function closeActive() { - if (activeMenu) - activeMenu.expanded = false; - - activeMenu = null; - activeTrigger = null; - } - - function forget(menu) { - if (activeMenu === menu) { - activeMenu = null; - activeTrigger = null; - } - } - - function hit(item, scenePos) { - if (!item || !item.visible) - return false; - - const p = item.mapFromItem(null, scenePos.x, scenePos.y); - return item.contains(p); - } - - function open(menu, trigger) { - if (activeMenu && activeMenu !== menu) - activeMenu.expanded = false; - - activeMenu = menu; - activeTrigger = trigger || null; - menu.expanded = true; - } - - function toggle(menu, trigger) { - if (activeMenu === menu && menu.expanded) - close(menu); - else - open(menu, trigger); - } -} diff --git a/Greeter/Helpers/SystemUsage.qml b/Greeter/Helpers/SystemUsage.qml deleted file mode 100644 index 11813f3..0000000 --- a/Greeter/Helpers/SystemUsage.qml +++ /dev/null @@ -1,409 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import QtQuick -import qs.Config - -Singleton { - id: root - - property string autoGpuType: "NONE" - property string cpuName: "" - property real cpuPerc - property real cpuTemp - - // Individual disks: Array of { mount, used, total, free, perc } - property var disks: [] - property real gpuMemTotal: 0 - property real gpuMemUsed - property real gpuPerc - property real gpuTemp - readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType - property real lastCpuIdle - property real lastCpuTotal - readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0 - property real memTotal - property real memUsed - property int refCount - readonly property real storagePerc: { - let totalUsed = 0; - let totalSize = 0; - for (const disk of disks) { - totalUsed += disk.used; - totalSize += disk.total; - } - return totalSize > 0 ? totalUsed / totalSize : 0; - } - - function cleanCpuName(name: string): string { - return name.replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/CPU/gi, "").replace(/\d+th Gen /gi, "").replace(/\d+nd Gen /gi, "").replace(/\d+rd Gen /gi, "").replace(/\d+st Gen /gi, "").replace(/Core /gi, "").replace(/Processor/gi, "").replace(/\s+/g, " ").trim(); - } - - function cleanGpuName(name: string): string { - return name.replace(/NVIDIA GeForce /gi, "").replace(/NVIDIA /gi, "").replace(/AMD Radeon /gi, "").replace(/AMD /gi, "").replace(/Intel /gi, "").replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/Graphics/gi, "").replace(/\s+/g, " ").trim(); - } - - function formatKib(kib: real): var { - const mib = 1024; - const gib = 1024 ** 2; - const tib = 1024 ** 3; - - if (kib >= tib) - return { - value: kib / tib, - unit: "TiB" - }; - if (kib >= gib) - return { - value: kib / gib, - unit: "GiB" - }; - if (kib >= mib) - return { - value: kib / mib, - unit: "MiB" - }; - return { - value: kib, - unit: "KiB" - }; - } - - Timer { - interval: Config.dashboard.resourceUpdateInterval - repeat: true - running: root.refCount > 0 - triggeredOnStart: true - - onTriggered: { - stat.reload(); - meminfo.reload(); - if (root.gpuType === "GENERIC") - gpuUsage.running = true; - } - } - - Timer { - interval: 60000 * 120 - repeat: true - running: true - triggeredOnStart: true - - onTriggered: { - storage.running = true; - } - } - - Timer { - interval: Config.dashboard.resourceUpdateInterval * 5 - repeat: true - running: root.refCount > 0 - triggeredOnStart: true - - onTriggered: { - sensors.running = true; - } - } - - FileView { - id: cpuinfoInit - - path: "/proc/cpuinfo" - - onLoaded: { - const nameMatch = text().match(/model name\s*:\s*(.+)/); - if (nameMatch) - root.cpuName = root.cleanCpuName(nameMatch[1]); - } - } - - FileView { - id: stat - - path: "/proc/stat" - - onLoaded: { - const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/); - if (data) { - const stats = data.slice(1).map(n => parseInt(n, 10)); - const total = stats.reduce((a, b) => a + b, 0); - const idle = stats[3] + (stats[4] ?? 0); - - const totalDiff = total - root.lastCpuTotal; - const idleDiff = idle - root.lastCpuIdle; - const newCpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0; - - root.lastCpuTotal = total; - root.lastCpuIdle = idle; - - if (Math.abs(newCpuPerc - root.cpuPerc) >= 0.01) - root.cpuPerc = newCpuPerc; - } - } - } - - FileView { - id: meminfo - - path: "/proc/meminfo" - - onLoaded: { - const data = text(); - const total = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1; - const used = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0; - - if (root.memTotal !== total) - root.memTotal = total; - - if (Math.abs(used - root.memUsed) >= 16384) - root.memUsed = used; - } - } - - Process { - id: storage - - command: ["lsblk", "-b", "-o", "NAME,SIZE,TYPE,FSUSED,FSSIZE", "-P"] - - stdout: StdioCollector { - onStreamFinished: { - const diskMap = {}; // Map disk name -> { name, totalSize, used, fsTotal } - const lines = text.trim().split("\n"); - - for (const line of lines) { - if (line.trim() === "") - continue; - const nameMatch = line.match(/NAME="([^"]+)"/); - const sizeMatch = line.match(/SIZE="([^"]+)"/); - const typeMatch = line.match(/TYPE="([^"]+)"/); - const fsusedMatch = line.match(/FSUSED="([^"]*)"/); - const fssizeMatch = line.match(/FSSIZE="([^"]*)"/); - - if (!nameMatch || !typeMatch) - continue; - - const name = nameMatch[1]; - const type = typeMatch[1]; - const size = parseInt(sizeMatch?.[1] || "0", 10); - const fsused = parseInt(fsusedMatch?.[1] || "0", 10); - const fssize = parseInt(fssizeMatch?.[1] || "0", 10); - - if (type === "disk") { - // Skip zram (swap) devices - if (name.startsWith("zram")) - continue; - - // Initialize disk entry - if (!diskMap[name]) { - diskMap[name] = { - name: name, - totalSize: size, - used: 0, - fsTotal: 0 - }; - } - } else if (type === "part") { - // Find parent disk (remove trailing numbers/p+numbers) - let parentDisk = name.replace(/p?\d+$/, ""); - // For nvme devices like nvme0n1p1, parent is nvme0n1 - if (name.match(/nvme\d+n\d+p\d+/)) - parentDisk = name.replace(/p\d+$/, ""); - - // Aggregate partition usage to parent disk - if (diskMap[parentDisk]) { - diskMap[parentDisk].used += fsused; - diskMap[parentDisk].fsTotal += fssize; - } - } - } - - const diskList = []; - let totalUsed = 0; - let totalSize = 0; - - for (const diskName of Object.keys(diskMap).sort()) { - const disk = diskMap[diskName]; - // Use filesystem total if available, otherwise use disk size - const total = disk.fsTotal > 0 ? disk.fsTotal : disk.totalSize; - const used = disk.used; - const perc = total > 0 ? used / total : 0; - - // Convert bytes to KiB for consistency with formatKib - diskList.push({ - mount: disk.name // Using 'mount' property for compatibility - , - used: used / 1024, - total: total / 1024, - free: (total - used) / 1024, - perc: perc - }); - - totalUsed += used; - totalSize += total; - } - - root.disks = diskList; - } - } - } - - Process { - id: gpuNameDetect - - command: ["sh", "-c", "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || lspci 2>/dev/null | grep -i 'vga\\|3d\\|display' | head -1"] - running: true - - stdout: StdioCollector { - onStreamFinished: { - const output = text.trim(); - if (!output) - return; - - // Check if it's from nvidia-smi (clean GPU name) - if (output.toLowerCase().includes("nvidia") || output.toLowerCase().includes("geforce") || output.toLowerCase().includes("rtx") || output.toLowerCase().includes("gtx")) { - root.gpuName = root.cleanGpuName(output); - } else { - // Parse lspci output: extract name from brackets or after colon - const bracketMatch = output.match(/\[([^\]]+)\]/); - if (bracketMatch) { - root.gpuName = root.cleanGpuName(bracketMatch[1]); - } else { - const colonMatch = output.match(/:\s*(.+)/); - if (colonMatch) - root.gpuName = root.cleanGpuName(colonMatch[1]); - } - } - } - } - } - - Process { - id: gpuTypeCheck - - command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"] - running: !Config.services.gpuType - - stdout: StdioCollector { - onStreamFinished: root.autoGpuType = text.trim() - } - } - - Process { - id: oneshotMem - - command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"] - running: root.gpuType === "NVIDIA" && root.gpuMemTotal === 0 - - stdout: StdioCollector { - onStreamFinished: { - root.gpuMemTotal = Number(this.text.trim()); - oneshotMem.running = false; - } - } - } - - Process { - id: gpuUsageNvidia - - command: ["/usr/bin/nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits", "-lms", "1000"] - running: root.refCount > 0 && root.gpuType === "NVIDIA" - - stdout: SplitParser { - onRead: data => { - const parts = String(data).trim().split(/\s*,\s*/); - if (parts.length < 3) - return; - - const usageRaw = parseInt(parts[0], 10); - const tempRaw = parseInt(parts[1], 10); - const memRaw = parseInt(parts[2], 10); - - if (!Number.isFinite(usageRaw) || !Number.isFinite(tempRaw) || !Number.isFinite(memRaw)) - return; - - const newGpuPerc = Math.max(0, Math.min(1, usageRaw / 100)); - const newGpuTemp = tempRaw; - const newGpuMemUsed = root.gpuMemTotal > 0 ? Math.max(0, Math.min(1, memRaw / root.gpuMemTotal)) : 0; - - // Only publish meaningful changes to avoid needless binding churn / repaints - if (Math.abs(root.gpuPerc - newGpuPerc) >= 0.01) - root.gpuPerc = newGpuPerc; - - if (Math.abs(root.gpuTemp - newGpuTemp) >= 1) - root.gpuTemp = newGpuTemp; - - if (Math.abs(root.gpuMemUsed - newGpuMemUsed) >= 0.01) - root.gpuMemUsed = newGpuMemUsed; - } - } - } - - Process { - id: gpuUsage - - command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : ["echo"] - - stdout: StdioCollector { - onStreamFinished: { - if (root.gpuType === "GENERIC") { - const percs = text.trim().split("\n"); - const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0); - root.gpuPerc = sum / percs.length / 100; - } else { - root.gpuPerc = 0; - root.gpuTemp = 0; - } - } - } - } - - Process { - id: sensors - - command: ["sensors"] - environment: ({ - LANG: "C.UTF-8", - LC_ALL: "C.UTF-8" - }) - - stdout: StdioCollector { - onStreamFinished: { - let cpuTemp = text.match(/(?:Package id [0-9]+|Tdie):\s+((\+|-)[0-9.]+)(°| )C/); - if (!cpuTemp) - // If AMD Tdie pattern failed, try fallback on Tctl - cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/); - - if (cpuTemp && Math.abs(parseFloat(cpuTemp[1]) - root.cpuTemp) >= 0.5) - root.cpuTemp = parseFloat(cpuTemp[1]); - - if (root.gpuType !== "GENERIC") - return; - - let eligible = false; - let sum = 0; - let count = 0; - - for (const line of text.trim().split("\n")) { - if (line === "Adapter: PCI adapter") - eligible = true; - else if (line === "") - eligible = false; - else if (eligible) { - let match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)(°| )C/); - if (!match) - // Fall back to junction/mem if GPU doesn't have edge temp (for AMD GPUs) - match = line.match(/^(junction|mem)+:\s+\+([0-9]+\.[0-9]+)(°| )C/); - - if (match) { - sum += parseFloat(match[2]); - count++; - } - } - } - - root.gpuTemp = count > 0 ? sum / count : 0; - } - } - } -} diff --git a/Greeter/Helpers/TextRender.qml b/Greeter/Helpers/TextRender.qml deleted file mode 100644 index bdc852c..0000000 --- a/Greeter/Helpers/TextRender.qml +++ /dev/null @@ -1,6 +0,0 @@ -import QtQuick - -Text { - renderType: Text.NativeRendering - textFormat: Text.PlainText -} diff --git a/Greeter/Helpers/ThemeIcons.qml b/Greeter/Helpers/ThemeIcons.qml deleted file mode 100644 index 62a3e8a..0000000 --- a/Greeter/Helpers/ThemeIcons.qml +++ /dev/null @@ -1,288 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell - -Singleton { - id: root - - property list entryList: [] - property var preppedIcons: [] - property var preppedIds: [] - property var preppedNames: [] - - // Dynamic fixups - property var regexSubstitutions: [ - { - "regex": /^steam_app_(\d+)$/, - "replace": "steam_icon_$1" - }, - { - "regex": /Minecraft.*/, - "replace": "minecraft-launcher" - }, - { - "regex": /.*polkit.*/, - "replace": "system-lock-screen" - }, - { - "regex": /gcr.prompter/, - "replace": "system-lock-screen" - } - ] - property real scoreThreshold: 0.2 - - // Manual overrides for tricky apps - property var substitutions: ({ - "code-url-handler": "visual-studio-code", - "Code": "visual-studio-code", - "gnome-tweaks": "org.gnome.tweaks", - "pavucontrol-qt": "pavucontrol", - "wps": "wps-office2019-kprometheus", - "wpsoffice": "wps-office2019-kprometheus", - "footclient": "foot" - }) - - function checkCleanMatch(str) { - if (!str || str.length <= 3) - return null; - if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId) - return null; - - // Aggressive fallback: strip all separators - const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, ''); - const list = Array.from(entryList); - - for (let i = 0; i < list.length; i++) { - const entry = list[i]; - const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, ''); - if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) { - return entry; - } - } - return null; - } - - function checkFuzzySearch(str) { - if (typeof FuzzySort === 'undefined') - return null; - - // Check filenames (IDs) first - if (preppedIds.length > 0) { - let results = fuzzyQuery(str, preppedIds); - if (results.length === 0) { - const underscored = str.replace(/-/g, '_').toLowerCase(); - if (underscored !== str) - results = fuzzyQuery(underscored, preppedIds); - } - if (results.length > 0) - return results[0]; - } - - // Then icons - if (preppedIcons.length > 0) { - const results = fuzzyQuery(str, preppedIcons); - if (results.length > 0) - return results[0]; - } - - // Then names - if (preppedNames.length > 0) { - const results = fuzzyQuery(str, preppedNames); - if (results.length > 0) - return results[0]; - } - - return null; - } - - // --- Lookup Helpers --- - - function checkHeuristic(str) { - if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) { - const entry = DesktopEntries.heuristicLookup(str); - if (entry) - return entry; - } - return null; - } - - function checkRegex(str) { - for (let i = 0; i < regexSubstitutions.length; i++) { - const sub = regexSubstitutions[i]; - const replaced = str.replace(sub.regex, sub.replace); - if (replaced !== str) { - return findAppEntry(replaced); - } - } - return null; - } - - function checkSimpleTransforms(str) { - if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId) - return null; - - const lower = str.toLowerCase(); - - const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()]; - - for (let i = 0; i < variants.length; i++) { - const variant = variants[i]; - if (variant) { - const entry = DesktopEntries.byId(variant); - if (entry) - return entry; - } - } - return null; - } - - function checkSubstitutions(str) { - let effectiveStr = substitutions[str]; - if (!effectiveStr) - effectiveStr = substitutions[str.toLowerCase()]; - - if (effectiveStr && effectiveStr !== str) { - return findAppEntry(effectiveStr); - } - return null; - } - - function distroLogoPath() { - try { - return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : ""; - } catch (e) { - return ""; - } - } - - // Robust lookup strategy - function findAppEntry(str) { - if (!str || str.length === 0) - return null; - - let result = null; - - if (result = checkHeuristic(str)) - return result; - if (result = checkSubstitutions(str)) - return result; - if (result = checkRegex(str)) - return result; - if (result = checkSimpleTransforms(str)) - return result; - if (result = checkFuzzySearch(str)) - return result; - if (result = checkCleanMatch(str)) - return result; - - return null; - } - - function fuzzyQuery(search, preppedData) { - if (!search || !preppedData || preppedData.length === 0) - return []; - return FuzzySort.go(search, preppedData, { - all: true, - key: "name" - }).map(r => r.obj.entry); - } - - function getFromReverseDomain(str) { - if (!str) - return ""; - return str.split('.').slice(-1)[0]; - } - - // Deprecated shim - function guessIcon(str) { - const entry = findAppEntry(str); - return entry ? entry.icon : "image-missing"; - } - - function iconExists(iconName) { - if (!iconName || iconName.length === 0) - return false; - if (iconName.startsWith("/")) - return true; - - const path = Quickshell.iconPath(iconName, true); - return path && path.length > 0 && !path.includes("image-missing"); - } - - function iconForAppId(appId, fallbackName) { - const fallback = fallbackName || "application-x-executable"; - if (!appId) - return iconFromName(fallback, fallback); - - const entry = findAppEntry(appId); - if (entry) { - return iconFromName(entry.icon, fallback); - } - - return iconFromName(appId, fallback); - } - - function iconFromName(iconName, fallbackName) { - const fallback = fallbackName || "application-x-executable"; - try { - if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) { - const p = Quickshell.iconPath(iconName, fallback); - if (p && p !== "") - return p; - } - } catch (e) {} - - try { - return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : ""; - } catch (e2) { - return ""; - } - } - - function normalizeWithHyphens(str) { - if (!str) - return ""; - return str.toLowerCase().replace(/\s+/g, "-"); - } - - function refreshEntries() { - if (typeof DesktopEntries === 'undefined') - return; - - const values = Array.from(DesktopEntries.applications.values); - if (values) { - entryList = values.sort((a, b) => a.name.localeCompare(b.name)); - updatePreppedData(); - } - } - - function updatePreppedData() { - if (typeof FuzzySort === 'undefined') - return; - - const list = Array.from(entryList); - preppedNames = list.map(a => ({ - name: FuzzySort.prepare(`${a.name} `), - entry: a - })); - preppedIcons = list.map(a => ({ - name: FuzzySort.prepare(`${a.icon} `), - entry: a - })); - preppedIds = list.map(a => ({ - name: FuzzySort.prepare(`${a.id} `), - entry: a - })); - } - - Component.onCompleted: refreshEntries() - - Connections { - function onValuesChanged() { - refreshEntries(); - } - - target: DesktopEntries.applications - } -} diff --git a/Greeter/Resources.qml b/Greeter/Resources.qml deleted file mode 100644 index c2f5c62..0000000 --- a/Greeter/Resources.qml +++ /dev/null @@ -1,77 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Helpers -import qs.Config - -GridLayout { - id: root - - anchors.left: parent.left - anchors.margins: Appearance.padding.large - anchors.right: parent.right - columnSpacing: Appearance.spacing.large - columns: 2 - rowSpacing: Appearance.spacing.large - rows: 1 - - Ref { - service: SystemUsage - } - - Resource { - Layout.bottomMargin: Appearance.padding.large - Layout.topMargin: Appearance.padding.large - colour: DynamicColors.palette.m3primary - icon: "memory" - value: SystemUsage.cpuPerc - } - - Resource { - Layout.bottomMargin: Appearance.padding.large - Layout.topMargin: Appearance.padding.large - colour: DynamicColors.palette.m3secondary - icon: "memory_alt" - value: SystemUsage.memPerc - } - - component Resource: CustomRect { - id: res - - required property color colour - required property string icon - required property real value - - Layout.fillWidth: true - color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2) - implicitHeight: width - radius: Appearance.rounding.large - - Behavior on value { - Anim { - duration: Appearance.anim.durations.large - } - } - - CircularProgress { - id: circ - - anchors.fill: parent - bgColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3) - fgColour: res.colour - padding: Appearance.padding.large * 3 - strokeWidth: width < 200 ? Appearance.padding.smaller : Appearance.padding.normal - value: res.value - } - - MaterialIcon { - id: icon - - anchors.centerIn: parent - color: res.colour - font.pointSize: (circ.arcRadius * 0.7) || 1 - font.weight: 600 - text: res.icon - } - } -} diff --git a/Helpers/Calendar.qml b/Helpers/Calendar.qml deleted file mode 100644 index 84180a8..0000000 --- a/Helpers/Calendar.qml +++ /dev/null @@ -1,97 +0,0 @@ -pragma Singleton - -import Quickshell -import qs.Helpers - -Singleton { - id: root - - property int displayMonth: new Date().getMonth() - property int displayYear: new Date().getFullYear() - readonly property int weekStartDay: 1 // 0 = Sunday, 1 = Monday - - function getISOWeekNumber(date: var): int { - const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); - const dayNum = d.getUTCDay() || 7; - d.setUTCDate(d.getUTCDate() + 4 - dayNum); - const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); - return Math.ceil((((d - yearStart) / 86400000) + 1) / 7); - } - - function getWeekNumbers(month: int, year: int): var { - const days = getWeeksForMonth(month, year); - const weekNumbers = []; - let lastWeekNumber = -1; - - for (let i = 0; i < days.length; i++) { - // Only add week numbers for days that belong to the current month - if (days[i].isCurrentMonth) { - const dayDate = new Date(days[i].year, days[i].month, days[i].day); - const weekNumber = getISOWeekNumber(dayDate); - - // Only push if this is a new week - if (weekNumber !== lastWeekNumber) { - weekNumbers.push(weekNumber); - lastWeekNumber = weekNumber; - } - } - } - - return weekNumbers; - } - - function getWeekStartIndex(month: int, year: int): int { - const today = new Date(); - if (today.getMonth() !== month || today.getFullYear() !== year) { - return 0; - } - - const days = getWeeksForMonth(month, year); - for (let i = 0; i < days.length; i++) { - if (days[i].isToday) { - // Return the start index of the week containing today - return Math.floor(i / 7) * 7; - } - } - - return 0; - } - - function getWeeksForMonth(month: int, year: int): var { - const firstDayOfMonth = new Date(year, month, 1); - const lastDayOfMonth = new Date(year, month + 1, 0); - - const days = []; - let currentDate = new Date(year, month, 1); - - // Back up to the start of the first week (Sunday or Monday based on locale) - const dayOfWeek = firstDayOfMonth.getDay(); - const daysToBackup = (dayOfWeek - root.weekStartDay + 7) % 7; - currentDate.setDate(currentDate.getDate() - daysToBackup); - - // Collect all days, including padding from previous/next month to complete the weeks - while (true) { - days.push({ - day: currentDate.getDate(), - month: currentDate.getMonth(), - year: currentDate.getFullYear(), - isCurrentMonth: currentDate.getMonth() === month, - isToday: isDateToday(currentDate) - }); - - currentDate.setDate(currentDate.getDate() + 1); - - // Stop after we've completed a full week following the last day of the month - if (currentDate > lastDayOfMonth && days.length % 7 === 0) { - break; - } - } - - return days; - } - - function isDateToday(date: var): bool { - const today = new Date(); - return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear(); - } -} diff --git a/Helpers/GetIcons.qml b/Helpers/GetIcons.qml deleted file mode 100644 index e957ad3..0000000 --- a/Helpers/GetIcons.qml +++ /dev/null @@ -1,17 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - id: root - - function getTrayIcon(id: string, icon: string): string { - if (icon.includes("?path=")) { - const [name, path] = icon.split("?path="); - icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`); - } else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") { - icon = icon.replace("qspixmap", "icon/discord-tray"); - } - return icon; - } -} diff --git a/Helpers/InitialTitle.qml b/Helpers/InitialTitle.qml deleted file mode 100644 index c0d832b..0000000 --- a/Helpers/InitialTitle.qml +++ /dev/null @@ -1,16 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Hyprland -import qs.Helpers - -Singleton { - function getInitialTitle(callback) { - let activeWindow = Hypr.activeToplevel.title; - let activeClass = Hypr.activeToplevel.lastIpcObject.class.toString(); - let regex = new RegExp(activeClass, "i"); - - const evalTitle = activeWindow.match(regex); - callback(evalTitle); - } -} diff --git a/Helpers/NotifCenterSpacing.qml b/Helpers/NotifCenterSpacing.qml deleted file mode 100644 index 3e9a6d6..0000000 --- a/Helpers/NotifCenterSpacing.qml +++ /dev/null @@ -1,16 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io - -Singleton { - id: root - - property alias centerX: notifCenterSpacing.centerX - - JsonAdapter { - id: notifCenterSpacing - - property int centerX - } -} diff --git a/Helpers/NotifPath.qml b/Helpers/NotifPath.qml deleted file mode 100644 index 5ce6db6..0000000 --- a/Helpers/NotifPath.qml +++ /dev/null @@ -1,16 +0,0 @@ -pragma Singleton - -import Quickshell.Io -import Quickshell - -Singleton { - id: root - - property alias notifPath: storage.notifPath - - JsonAdapter { - id: storage - - property string notifPath: Quickshell.statePath("notifications.json") - } -} diff --git a/Helpers/PixelSnap.qml b/Helpers/PixelSnap.qml deleted file mode 100644 index 96372a1..0000000 --- a/Helpers/PixelSnap.qml +++ /dev/null @@ -1,6 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { -} diff --git a/Helpers/SearchWallpapers.qml b/Helpers/SearchWallpapers.qml deleted file mode 100644 index 631b638..0000000 --- a/Helpers/SearchWallpapers.qml +++ /dev/null @@ -1,49 +0,0 @@ -pragma Singleton - -import Quickshell -import Quickshell.Io -import ZShell.Models -import qs.Config -import qs.Modules -import qs.Helpers -import qs.Paths - -Searcher { - id: root - - property string actualCurrent: WallpaperPath.currentWallpaperPath - readonly property string current: showPreview ? previewPath : actualCurrent - property string previewPath - property bool showPreview: false - - function preview(path: string): void { - previewPath = path; - showPreview = true; - } - - function setWallpaper(path: string): void { - actualCurrent = path; - WallpaperPath.currentWallpaperPath = path; - Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/LockScreenBg.py")} --input_image=${root.actualCurrent} --output_path=${Paths.state}/lockscreen_bg.png`]); - } - - function stopPreview(): void { - showPreview = false; - Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/SchemeColorGen.py")} --path=${root.actualCurrent} --thumbnail=${Paths.cache}/imagecache/thumbnail.jpg --output=${Paths.state}/scheme.json --scheme=${Config.colors.schemeType}`]); - } - - extraOpts: useFuzzy ? ({}) : ({ - forward: false - }) - key: "relativePath" - list: wallpapers.entries - useFuzzy: true - - FileSystemModel { - id: wallpapers - - filter: FileSystemModel.Images - path: Config.general.wallpaperPath - recursive: true - } -} diff --git a/Helpers/SettingsDropdowns.qml b/Helpers/SettingsDropdowns.qml deleted file mode 100644 index 4a4ac27..0000000 --- a/Helpers/SettingsDropdowns.qml +++ /dev/null @@ -1,60 +0,0 @@ -pragma Singleton -import QtQuick - -QtObject { - id: root - - property Item activeMenu: null - property Item activeTrigger: null - - function close(menu) { - if (!menu) - return; - - if (activeMenu === menu) { - activeMenu = null; - activeTrigger = null; - } - - menu.expanded = false; - } - - function closeActive() { - if (activeMenu) - activeMenu.expanded = false; - - activeMenu = null; - activeTrigger = null; - } - - function forget(menu) { - if (activeMenu === menu) { - activeMenu = null; - activeTrigger = null; - } - } - - function hit(item, scenePos) { - if (!item || !item.visible) - return false; - - const p = item.mapFromItem(null, scenePos.x, scenePos.y); - return item.contains(p); - } - - function open(menu, trigger) { - if (activeMenu && activeMenu !== menu) - activeMenu.expanded = false; - - activeMenu = menu; - activeTrigger = trigger || null; - menu.expanded = true; - } - - function toggle(menu, trigger) { - if (activeMenu === menu && menu.expanded) - close(menu); - else - open(menu, trigger); - } -} diff --git a/Helpers/SettingsHighlight.qml b/Helpers/SettingsHighlight.qml deleted file mode 100644 index 535eb0f..0000000 --- a/Helpers/SettingsHighlight.qml +++ /dev/null @@ -1,27 +0,0 @@ -pragma Singleton - -import Quickshell -import QtQuick - -Singleton { - id: root - - property string highlightedSetting: "" - - function clear() { - highlightedSetting = ""; - } - - function highlight(settingName: string) { - highlightedSetting = settingName; - highlightTimer.restart(); - } - - Timer { - id: highlightTimer - - interval: 2000 - - onTriggered: root.clear() - } -} diff --git a/Helpers/TextRender.qml b/Helpers/TextRender.qml deleted file mode 100644 index bdc852c..0000000 --- a/Helpers/TextRender.qml +++ /dev/null @@ -1,6 +0,0 @@ -import QtQuick - -Text { - renderType: Text.NativeRendering - textFormat: Text.PlainText -} diff --git a/Helpers/ThemeIcons.qml b/Helpers/ThemeIcons.qml deleted file mode 100644 index 62a3e8a..0000000 --- a/Helpers/ThemeIcons.qml +++ /dev/null @@ -1,288 +0,0 @@ -pragma Singleton - -import QtQuick -import Quickshell - -Singleton { - id: root - - property list entryList: [] - property var preppedIcons: [] - property var preppedIds: [] - property var preppedNames: [] - - // Dynamic fixups - property var regexSubstitutions: [ - { - "regex": /^steam_app_(\d+)$/, - "replace": "steam_icon_$1" - }, - { - "regex": /Minecraft.*/, - "replace": "minecraft-launcher" - }, - { - "regex": /.*polkit.*/, - "replace": "system-lock-screen" - }, - { - "regex": /gcr.prompter/, - "replace": "system-lock-screen" - } - ] - property real scoreThreshold: 0.2 - - // Manual overrides for tricky apps - property var substitutions: ({ - "code-url-handler": "visual-studio-code", - "Code": "visual-studio-code", - "gnome-tweaks": "org.gnome.tweaks", - "pavucontrol-qt": "pavucontrol", - "wps": "wps-office2019-kprometheus", - "wpsoffice": "wps-office2019-kprometheus", - "footclient": "foot" - }) - - function checkCleanMatch(str) { - if (!str || str.length <= 3) - return null; - if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId) - return null; - - // Aggressive fallback: strip all separators - const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, ''); - const list = Array.from(entryList); - - for (let i = 0; i < list.length; i++) { - const entry = list[i]; - const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, ''); - if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) { - return entry; - } - } - return null; - } - - function checkFuzzySearch(str) { - if (typeof FuzzySort === 'undefined') - return null; - - // Check filenames (IDs) first - if (preppedIds.length > 0) { - let results = fuzzyQuery(str, preppedIds); - if (results.length === 0) { - const underscored = str.replace(/-/g, '_').toLowerCase(); - if (underscored !== str) - results = fuzzyQuery(underscored, preppedIds); - } - if (results.length > 0) - return results[0]; - } - - // Then icons - if (preppedIcons.length > 0) { - const results = fuzzyQuery(str, preppedIcons); - if (results.length > 0) - return results[0]; - } - - // Then names - if (preppedNames.length > 0) { - const results = fuzzyQuery(str, preppedNames); - if (results.length > 0) - return results[0]; - } - - return null; - } - - // --- Lookup Helpers --- - - function checkHeuristic(str) { - if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) { - const entry = DesktopEntries.heuristicLookup(str); - if (entry) - return entry; - } - return null; - } - - function checkRegex(str) { - for (let i = 0; i < regexSubstitutions.length; i++) { - const sub = regexSubstitutions[i]; - const replaced = str.replace(sub.regex, sub.replace); - if (replaced !== str) { - return findAppEntry(replaced); - } - } - return null; - } - - function checkSimpleTransforms(str) { - if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId) - return null; - - const lower = str.toLowerCase(); - - const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()]; - - for (let i = 0; i < variants.length; i++) { - const variant = variants[i]; - if (variant) { - const entry = DesktopEntries.byId(variant); - if (entry) - return entry; - } - } - return null; - } - - function checkSubstitutions(str) { - let effectiveStr = substitutions[str]; - if (!effectiveStr) - effectiveStr = substitutions[str.toLowerCase()]; - - if (effectiveStr && effectiveStr !== str) { - return findAppEntry(effectiveStr); - } - return null; - } - - function distroLogoPath() { - try { - return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : ""; - } catch (e) { - return ""; - } - } - - // Robust lookup strategy - function findAppEntry(str) { - if (!str || str.length === 0) - return null; - - let result = null; - - if (result = checkHeuristic(str)) - return result; - if (result = checkSubstitutions(str)) - return result; - if (result = checkRegex(str)) - return result; - if (result = checkSimpleTransforms(str)) - return result; - if (result = checkFuzzySearch(str)) - return result; - if (result = checkCleanMatch(str)) - return result; - - return null; - } - - function fuzzyQuery(search, preppedData) { - if (!search || !preppedData || preppedData.length === 0) - return []; - return FuzzySort.go(search, preppedData, { - all: true, - key: "name" - }).map(r => r.obj.entry); - } - - function getFromReverseDomain(str) { - if (!str) - return ""; - return str.split('.').slice(-1)[0]; - } - - // Deprecated shim - function guessIcon(str) { - const entry = findAppEntry(str); - return entry ? entry.icon : "image-missing"; - } - - function iconExists(iconName) { - if (!iconName || iconName.length === 0) - return false; - if (iconName.startsWith("/")) - return true; - - const path = Quickshell.iconPath(iconName, true); - return path && path.length > 0 && !path.includes("image-missing"); - } - - function iconForAppId(appId, fallbackName) { - const fallback = fallbackName || "application-x-executable"; - if (!appId) - return iconFromName(fallback, fallback); - - const entry = findAppEntry(appId); - if (entry) { - return iconFromName(entry.icon, fallback); - } - - return iconFromName(appId, fallback); - } - - function iconFromName(iconName, fallbackName) { - const fallback = fallbackName || "application-x-executable"; - try { - if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) { - const p = Quickshell.iconPath(iconName, fallback); - if (p && p !== "") - return p; - } - } catch (e) {} - - try { - return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : ""; - } catch (e2) { - return ""; - } - } - - function normalizeWithHyphens(str) { - if (!str) - return ""; - return str.toLowerCase().replace(/\s+/g, "-"); - } - - function refreshEntries() { - if (typeof DesktopEntries === 'undefined') - return; - - const values = Array.from(DesktopEntries.applications.values); - if (values) { - entryList = values.sort((a, b) => a.name.localeCompare(b.name)); - updatePreppedData(); - } - } - - function updatePreppedData() { - if (typeof FuzzySort === 'undefined') - return; - - const list = Array.from(entryList); - preppedNames = list.map(a => ({ - name: FuzzySort.prepare(`${a.name} `), - entry: a - })); - preppedIcons = list.map(a => ({ - name: FuzzySort.prepare(`${a.icon} `), - entry: a - })); - preppedIds = list.map(a => ({ - name: FuzzySort.prepare(`${a.id} `), - entry: a - })); - } - - Component.onCompleted: refreshEntries() - - Connections { - function onValuesChanged() { - refreshEntries(); - } - - target: DesktopEntries.applications - } -} diff --git a/Helpers/Year.qml b/Helpers/Year.qml deleted file mode 100644 index 5dec12d..0000000 --- a/Helpers/Year.qml +++ /dev/null @@ -1,56 +0,0 @@ -import Quickshell.Io - -JsonObject { - property list week_0: [0, 0, 0, 0, 0, 0,] - property list week_1: [0, 0, 0, 0, 0, 0,] - property list week_10: [0, 0, 0, 0, 0, 0,] - property list week_11: [0, 0, 0, 0, 0, 0,] - property list week_12: [0, 0, 0, 0, 0, 0,] - property list week_13: [0, 0, 0, 0, 0, 0,] - property list week_14: [0, 0, 0, 0, 0, 0,] - property list week_15: [0, 0, 0, 0, 0, 0,] - property list week_16: [0, 0, 0, 0, 0, 0,] - property list week_17: [0, 0, 0, 0, 0, 0,] - property list week_18: [0, 0, 0, 0, 0, 0,] - property list week_19: [0, 0, 0, 0, 0, 0,] - property list week_2: [0, 0, 0, 0, 0, 0,] - property list week_20: [0, 0, 0, 0, 0, 0,] - property list week_21: [0, 0, 0, 0, 0, 0,] - property list week_22: [0, 0, 0, 0, 0, 0,] - property list week_23: [0, 0, 0, 0, 0, 0,] - property list week_24: [0, 0, 0, 0, 0, 0,] - property list week_25: [0, 0, 0, 0, 0, 0,] - property list week_26: [0, 0, 0, 0, 0, 0,] - property list week_27: [0, 0, 0, 0, 0, 0,] - property list week_28: [0, 0, 0, 0, 0, 0,] - property list week_29: [0, 0, 0, 0, 0, 0,] - property list week_3: [0, 0, 0, 0, 0, 0,] - property list week_30: [0, 0, 0, 0, 0, 0,] - property list week_31: [0, 0, 0, 0, 0, 0,] - property list week_32: [0, 0, 0, 0, 0, 0,] - property list week_33: [0, 0, 0, 0, 0, 0,] - property list week_34: [0, 0, 0, 0, 0, 0,] - property list week_35: [0, 0, 0, 0, 0, 0,] - property list week_36: [0, 0, 0, 0, 0, 0,] - property list week_37: [0, 0, 0, 0, 0, 0,] - property list week_38: [0, 0, 0, 0, 0, 0,] - property list week_39: [0, 0, 0, 0, 0, 0,] - property list week_4: [0, 0, 0, 0, 0, 0,] - property list week_40: [0, 0, 0, 0, 0, 0,] - property list week_41: [0, 0, 0, 0, 0, 0,] - property list week_42: [0, 0, 0, 0, 0, 0,] - property list week_43: [0, 0, 0, 0, 0, 0,] - property list week_44: [0, 0, 0, 0, 0, 0,] - property list week_45: [0, 0, 0, 0, 0, 0,] - property list week_46: [0, 0, 0, 0, 0, 0,] - property list week_47: [0, 0, 0, 0, 0, 0,] - property list week_48: [0, 0, 0, 0, 0, 0,] - property list week_49: [0, 0, 0, 0, 0, 0,] - property list week_5: [0, 0, 0, 0, 0, 0,] - property list week_50: [0, 0, 0, 0, 0, 0,] - property list week_51: [0, 0, 0, 0, 0, 0,] - property list week_6: [0, 0, 0, 0, 0, 0,] - property list week_7: [0, 0, 0, 0, 0, 0,] - property list week_8: [0, 0, 0, 0, 0, 0,] - property list week_9: [0, 0, 0, 0, 0, 0,] -} diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index f9ffc11..ac293c6 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -47,7 +47,7 @@ RowLayout { return; } - if (!popouts.currentName.startsWith("traymenu")) + if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) popouts.hasCurrent = false; } @@ -56,19 +56,7 @@ RowLayout { const item = ch.item; const itemWidth = item.implicitWidth; - if (id === "audio" && Config.bar.popouts.audio) { - popouts.currentName = "audio"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } else if (id === "network" && Config.bar.popouts.network) { - popouts.currentName = "network"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } else if (id === "upower" && Config.bar.popouts.upower) { - popouts.currentName = "upower"; - popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); - popouts.hasCurrent = true; - } else if (id === "updates") { + if (id === "updates") { popouts.currentName = "updates"; popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x); popouts.hasCurrent = true; @@ -80,7 +68,6 @@ RowLayout { Repeater { id: repeater - // model: Config.bar.entries.filted(n => n.index > 50).sort(n => n.index) model: Config.bar.entries DelegateChooser { diff --git a/Modules/Notifications/Sidebar/Utils/Cards/Record.qml b/Modules/Notifications/Sidebar/Utils/Cards/Record.qml index 1d12b0a..2cf99a7 100644 --- a/Modules/Notifications/Sidebar/Utils/Cards/Record.qml +++ b/Modules/Notifications/Sidebar/Utils/Cards/Record.qml @@ -133,7 +133,7 @@ CustomRect { ParallelAnimation { Anim { duration: Appearance.anim.durations.small - easing: Appearance.anim.curves.standardAccel + easing.bezierCurve: Appearance.anim.curves.standardAccel property: "scale" target: listOrControls to: 0.7 @@ -141,7 +141,7 @@ CustomRect { Anim { duration: Appearance.anim.durations.small - easing: Appearance.anim.curves.standardAccel + easing.bezierCurve: Appearance.anim.curves.standardAccel property: "opacity" target: listOrControls to: 0 @@ -166,7 +166,7 @@ CustomRect { ParallelAnimation { Anim { duration: Appearance.anim.durations.small - easing: Appearance.anim.curves.standardDecel + easing.bezierCurve: Appearance.anim.curves.standardDecel property: "scale" target: listOrControls to: 1 @@ -174,7 +174,7 @@ CustomRect { Anim { duration: Appearance.anim.durations.small - easing: Appearance.anim.curves.standardDecel + easing.bezierCurve: Appearance.anim.curves.standardDecel property: "opacity" target: listOrControls to: 1 @@ -217,14 +217,14 @@ CustomRect { Anim { duration: Appearance.anim.durations.large - easing: Appearance.anim.curves.emphasizedAccel + easing.bezierCurve: Appearance.anim.curves.emphasizedAccel from: 1 to: 0 } Anim { duration: Appearance.anim.durations.extraLarge - easing: Appearance.anim.curves.emphasizedDecel + easing.bezierCurve: Appearance.anim.curves.emphasizedDecel from: 0 to: 1 } diff --git a/Modules/Polkit/Polkit.qml b/Modules/Polkit/Polkit.qml index 8485c8e..9b50509 100644 --- a/Modules/Polkit/Polkit.qml +++ b/Modules/Polkit/Polkit.qml @@ -1,8 +1,8 @@ import Quickshell import Quickshell.Services.Polkit import Quickshell.Wayland -import Quickshell.Hyprland import Quickshell.Widgets +import ZShell.Components import QtQuick import QtQuick.Layouts import QtQuick.Controls @@ -243,17 +243,16 @@ Scope { Layout.preferredWidth: contentRow.implicitWidth spacing: 8 - CustomButton { + IconTextButton { id: detailsButton Layout.alignment: Qt.AlignLeft - Layout.preferredHeight: 40 - Layout.preferredWidth: 92 - bgColor: DynamicColors.palette.m3surfaceContainer - enabled: true - radius: Appearance.rounding.full + icon: "info" + inactiveColor: DynamicColors.palette.m3surfaceContainer + inactiveOnColor: DynamicColors.palette.m3onSurface + isRound: true + shapeMorph: true text: "Details" - textColor: DynamicColors.palette.m3onSurface onClicked: { panelWindow.detailsOpen = !panelWindow.detailsOpen; @@ -266,41 +265,50 @@ Scope { Layout.fillWidth: true } - CustomButton { - id: okButton - + ButtonRow { Layout.alignment: Qt.AlignRight - Layout.preferredHeight: 40 - Layout.preferredWidth: 76 - bgColor: DynamicColors.palette.m3primary - enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired - radius: Appearance.rounding.full - text: "OK" - textColor: DynamicColors.palette.m3onPrimary + spacing: Appearance.spacing.normal - onClicked: { - polkitAgent.flow.submit(passInput.text); - passInput.text = ""; - passInput.forceActiveFocus(); + IconTextButton { + id: okButton + + enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired + horizontalPadding: Appearance.padding.large + icon: "check" + inactiveColor: DynamicColors.palette.m3primary + inactiveOnColor: DynamicColors.palette.m3onPrimary + isRound: true + isToggle: false + shapeMorph: true + shapeMorphExpansion: pressed ? 12 : 0 + text: "OK" + + onClicked: { + polkitAgent.flow.submit(passInput.text); + passInput.text = ""; + passInput.forceActiveFocus(); + } } - } - CustomButton { - id: cancelButton + IconTextButton { + id: cancelButton - Layout.alignment: Qt.AlignRight - Layout.preferredHeight: 40 - Layout.preferredWidth: 76 - bgColor: DynamicColors.palette.m3surfaceContainer - enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired - radius: Appearance.rounding.full - text: "Cancel" - textColor: DynamicColors.palette.m3onSurface + enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired + horizontalPadding: Appearance.padding.large + icon: "close" + inactiveColor: DynamicColors.palette.m3surfaceContainer + inactiveOnColor: DynamicColors.palette.m3onSurface + isRound: true + isToggle: false + shapeMorph: true + shapeMorphExpansion: pressed ? 12 : 0 + text: "Cancel" - onClicked: { - root.shouldShow = false; - polkitAgent.flow.cancelAuthenticationRequest(); - passInput.text = ""; + onClicked: { + root.shouldShow = false; + polkitAgent.flow.cancelAuthenticationRequest(); + passInput.text = ""; + } } } } diff --git a/Modules/Settings/Pages/Panels/Bar/BarTray.qml b/Modules/Settings/Pages/Panels/Bar/BarTray.qml index 2fe962c..023640d 100644 --- a/Modules/Settings/Pages/Panels/Bar/BarTray.qml +++ b/Modules/Settings/Pages/Panels/Bar/BarTray.qml @@ -19,7 +19,6 @@ PageBase { SpinRow { first: true from: 12 - last: true settingAnchor: "bar-tray-iconsize" stepSize: 1 text: qsTr("Icon size") @@ -28,5 +27,24 @@ PageBase { onMoved: value => Config.bar.tray.trayIconSize = value } + + ToggleRow { + checked: Config.bar.tray.showOnHover + settingAnchor: "bar-tray-show-popout-on-hover" + subtext: Config.bar.tray.showOnHover ? qsTr("Will show context menu on hover") : qsTr("Will show context menu on right-click") + text: qsTr("Show popout on hover") + + onToggled: Config.bar.tray.showOnHover = checked + } + + ToggleRow { + checked: Config.bar.tray.recolorIcons + last: true + settingAnchor: "bar-tray-recolor-icons" + subtext: qsTr("Recolors icons to fit current scheme") + text: qsTr("Recolor icons") + + onToggled: Config.bar.tray.recolorIcons = checked + } } } diff --git a/Modules/SysTray/TrayItem.qml b/Modules/SysTray/TrayItem.qml index c0f80bc..dc017c4 100644 --- a/Modules/SysTray/TrayItem.qml +++ b/Modules/SysTray/TrayItem.qml @@ -45,7 +45,8 @@ Item { CustomRect { anchors.fill: parent anchors.margins: 3 - color: icon.layer.enabled && root.current ? DynamicColors.palette.m3primary : "transparent" + color: icon.layer.enabled && enabled && root.current ? DynamicColors.palette.m3primary : "transparent" + enabled: !Config.bar.tray.showOnHover radius: Appearance.rounding.full StateLayer { @@ -57,7 +58,7 @@ Item { if (mouse.button === Qt.LeftButton) { root.item.activate(); console.log(icon.source + "\n" + root.item.id); - } else if (mouse.button === Qt.RightButton && Config.bar.popouts.tray) { + } else if (mouse.button === Qt.RightButton && Config.bar.popouts.tray && !Config.bar.tray.showOnHover) { root.popouts.currentName = `traymenu${root.ind}`; root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x); root.popouts.hasCurrent = true; @@ -76,9 +77,9 @@ Item { anchors.centerIn: parent antialiasing: true - color: root.current ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface + color: root.current && !Config.bar.tray.showOnHover ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface implicitSize: Config.bar.tray.trayIconSize * root.dpr - layer.enabled: Config.general.color.smart || Config.general.color.scheduleDark + layer.enabled: Config.bar.tray.recolorIcons scale: 1 / root.dpr source: root.resolveIcon(root.item.id, root.item.icon) } diff --git a/Modules/SysTray/TrayWidget.qml b/Modules/SysTray/TrayWidget.qml index c89617b..a9c7ea8 100644 --- a/Modules/SysTray/TrayWidget.qml +++ b/Modules/SysTray/TrayWidget.qml @@ -66,12 +66,12 @@ RowLayout { } let trayPos = mapToItem(sysTray, localX, localY); - if (sysTray.contains(Qt.point(trayPos.x, trayPos.y))) { + if (Config.bar.tray.showOnHover && sysTray.contains(Qt.point(trayPos.x, trayPos.y))) { let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y); let child = sysRow.childAt(trayRowPos.x, trayRowPos.y); - if (child && child.hasOwnProperty("popoutId")) { + if (child && child.hasOwnProperty("ind")) { return { - id: child.popoutId, + id: `traymenu${child.ind}`, item: child }; }