chore: cleanup more unused files/imports, use new buttonrow for polkit

This commit is contained in:
2026-07-02 13:35:17 +02:00
parent 2b06a33508
commit c62695bfff
30 changed files with 45 additions and 2308 deletions
-135
View File
@@ -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
}
}
}
}
-70
View File
@@ -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
}
}
}
-69
View File
@@ -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
}
]
}
}
}
}
}
-32
View File
@@ -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();
}
}
}
-169
View File
@@ -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
}
}
}
-25
View File
@@ -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
}
}
-54
View File
@@ -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
}
}
}
-44
View File
@@ -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)
}
}
-33
View File
@@ -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;
}
}
}
-309
View File
@@ -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
}
}
}
-76
View File
@@ -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
}
}
-180
View File
@@ -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
}
}
}
-50
View File
@@ -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);
}
}
}
}
-17
View File
@@ -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;
}
}
-60
View File
@@ -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);
}
}
-6
View File
@@ -1,6 +0,0 @@
import QtQuick
Text {
renderType: Text.NativeRendering
textFormat: Text.PlainText
}
-288
View File
@@ -1,288 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
Singleton {
id: root
property list<DesktopEntry> 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
}
}
-97
View File
@@ -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();
}
}
-17
View File
@@ -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;
}
}
-16
View File
@@ -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);
}
}
-16
View File
@@ -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
}
}
-16
View File
@@ -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")
}
}
-6
View File
@@ -1,6 +0,0 @@
pragma Singleton
import Quickshell
Singleton {
}
-49
View File
@@ -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
}
}
-60
View File
@@ -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);
}
}
-27
View File
@@ -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()
}
}
-6
View File
@@ -1,6 +0,0 @@
import QtQuick
Text {
renderType: Text.NativeRendering
textFormat: Text.PlainText
}
-288
View File
@@ -1,288 +0,0 @@
pragma Singleton
import QtQuick
import Quickshell
Singleton {
id: root
property list<DesktopEntry> 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
}
}
-56
View File
@@ -1,56 +0,0 @@
import Quickshell.Io
JsonObject {
property list<var> week_0: [0, 0, 0, 0, 0, 0,]
property list<var> week_1: [0, 0, 0, 0, 0, 0,]
property list<var> week_10: [0, 0, 0, 0, 0, 0,]
property list<var> week_11: [0, 0, 0, 0, 0, 0,]
property list<var> week_12: [0, 0, 0, 0, 0, 0,]
property list<var> week_13: [0, 0, 0, 0, 0, 0,]
property list<var> week_14: [0, 0, 0, 0, 0, 0,]
property list<var> week_15: [0, 0, 0, 0, 0, 0,]
property list<var> week_16: [0, 0, 0, 0, 0, 0,]
property list<var> week_17: [0, 0, 0, 0, 0, 0,]
property list<var> week_18: [0, 0, 0, 0, 0, 0,]
property list<var> week_19: [0, 0, 0, 0, 0, 0,]
property list<var> week_2: [0, 0, 0, 0, 0, 0,]
property list<var> week_20: [0, 0, 0, 0, 0, 0,]
property list<var> week_21: [0, 0, 0, 0, 0, 0,]
property list<var> week_22: [0, 0, 0, 0, 0, 0,]
property list<var> week_23: [0, 0, 0, 0, 0, 0,]
property list<var> week_24: [0, 0, 0, 0, 0, 0,]
property list<var> week_25: [0, 0, 0, 0, 0, 0,]
property list<var> week_26: [0, 0, 0, 0, 0, 0,]
property list<var> week_27: [0, 0, 0, 0, 0, 0,]
property list<var> week_28: [0, 0, 0, 0, 0, 0,]
property list<var> week_29: [0, 0, 0, 0, 0, 0,]
property list<var> week_3: [0, 0, 0, 0, 0, 0,]
property list<var> week_30: [0, 0, 0, 0, 0, 0,]
property list<var> week_31: [0, 0, 0, 0, 0, 0,]
property list<var> week_32: [0, 0, 0, 0, 0, 0,]
property list<var> week_33: [0, 0, 0, 0, 0, 0,]
property list<var> week_34: [0, 0, 0, 0, 0, 0,]
property list<var> week_35: [0, 0, 0, 0, 0, 0,]
property list<var> week_36: [0, 0, 0, 0, 0, 0,]
property list<var> week_37: [0, 0, 0, 0, 0, 0,]
property list<var> week_38: [0, 0, 0, 0, 0, 0,]
property list<var> week_39: [0, 0, 0, 0, 0, 0,]
property list<var> week_4: [0, 0, 0, 0, 0, 0,]
property list<var> week_40: [0, 0, 0, 0, 0, 0,]
property list<var> week_41: [0, 0, 0, 0, 0, 0,]
property list<var> week_42: [0, 0, 0, 0, 0, 0,]
property list<var> week_43: [0, 0, 0, 0, 0, 0,]
property list<var> week_44: [0, 0, 0, 0, 0, 0,]
property list<var> week_45: [0, 0, 0, 0, 0, 0,]
property list<var> week_46: [0, 0, 0, 0, 0, 0,]
property list<var> week_47: [0, 0, 0, 0, 0, 0,]
property list<var> week_48: [0, 0, 0, 0, 0, 0,]
property list<var> week_49: [0, 0, 0, 0, 0, 0,]
property list<var> week_5: [0, 0, 0, 0, 0, 0,]
property list<var> week_50: [0, 0, 0, 0, 0, 0,]
property list<var> week_51: [0, 0, 0, 0, 0, 0,]
property list<var> week_6: [0, 0, 0, 0, 0, 0,]
property list<var> week_7: [0, 0, 0, 0, 0, 0,]
property list<var> week_8: [0, 0, 0, 0, 0, 0,]
property list<var> week_9: [0, 0, 0, 0, 0, 0,]
}
+45 -37
View File
@@ -1,8 +1,8 @@
import Quickshell import Quickshell
import Quickshell.Services.Polkit import Quickshell.Services.Polkit
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets import Quickshell.Widgets
import ZShell.Components
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
@@ -243,17 +243,16 @@ Scope {
Layout.preferredWidth: contentRow.implicitWidth Layout.preferredWidth: contentRow.implicitWidth
spacing: 8 spacing: 8
CustomButton { IconTextButton {
id: detailsButton id: detailsButton
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
Layout.preferredHeight: 40 icon: "info"
Layout.preferredWidth: 92 inactiveColor: DynamicColors.palette.m3surfaceContainer
bgColor: DynamicColors.palette.m3surfaceContainer inactiveOnColor: DynamicColors.palette.m3onSurface
enabled: true isRound: true
radius: Appearance.rounding.full shapeMorph: true
text: "Details" text: "Details"
textColor: DynamicColors.palette.m3onSurface
onClicked: { onClicked: {
panelWindow.detailsOpen = !panelWindow.detailsOpen; panelWindow.detailsOpen = !panelWindow.detailsOpen;
@@ -266,41 +265,50 @@ Scope {
Layout.fillWidth: true Layout.fillWidth: true
} }
CustomButton { ButtonRow {
id: okButton
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
Layout.preferredHeight: 40 spacing: Appearance.spacing.normal
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
onClicked: { IconTextButton {
polkitAgent.flow.submit(passInput.text); id: okButton
passInput.text = "";
passInput.forceActiveFocus(); 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 { IconTextButton {
id: cancelButton id: cancelButton
Layout.alignment: Qt.AlignRight enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
Layout.preferredHeight: 40 horizontalPadding: Appearance.padding.large
Layout.preferredWidth: 76 icon: "close"
bgColor: DynamicColors.palette.m3surfaceContainer inactiveColor: DynamicColors.palette.m3surfaceContainer
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired inactiveOnColor: DynamicColors.palette.m3onSurface
radius: Appearance.rounding.full isRound: true
text: "Cancel" isToggle: false
textColor: DynamicColors.palette.m3onSurface shapeMorph: true
shapeMorphExpansion: pressed ? 12 : 0
text: "Cancel"
onClicked: { onClicked: {
root.shouldShow = false; root.shouldShow = false;
polkitAgent.flow.cancelAuthenticationRequest(); polkitAgent.flow.cancelAuthenticationRequest();
passInput.text = ""; passInput.text = "";
}
} }
} }
} }