Compare commits
15
Commits
v0.2.0
...
5e78b3b492
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e78b3b492 | ||
|
|
d013be0f9a | ||
|
|
69774e7759 | ||
|
|
cdf66bff23 | ||
|
|
562770595f | ||
|
|
9126b04b10 | ||
|
|
55dab8f7ce | ||
|
|
c62695bfff | ||
|
|
2b06a33508 | ||
|
|
4d731c5a65 | ||
|
|
53836aec1a | ||
|
|
355106cb2b | ||
|
|
865b5bda53 | ||
|
|
2b89c8e4a1 | ||
|
|
61019204d8 |
@@ -51,6 +51,20 @@ add_compile_options(
|
|||||||
-Wunreachable-code
|
-Wunreachable-code
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if("shell" IN_LIST ENABLE_MODULES)
|
||||||
|
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||||
|
set(SETTINGS_INDEX_JSON "${CMAKE_BINARY_DIR}/settings-index.json")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
|
"${CMAKE_SOURCE_DIR}/scripts/build-settings-index.py"
|
||||||
|
"${CMAKE_SOURCE_DIR}/Modules/Settings"
|
||||||
|
"${SETTINGS_INDEX_JSON}"
|
||||||
|
RESULT_VARIABLE SETTINGS_INDEX_RESULT
|
||||||
|
)
|
||||||
|
if(NOT SETTINGS_INDEX_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to build settings search index")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if("plugin" IN_LIST ENABLE_MODULES)
|
if("plugin" IN_LIST ENABLE_MODULES)
|
||||||
add_subdirectory(Plugins)
|
add_subdirectory(Plugins)
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -72,10 +72,12 @@ JsonObject {
|
|||||||
property bool upower: true
|
property bool upower: true
|
||||||
}
|
}
|
||||||
component Tray: JsonObject {
|
component Tray: JsonObject {
|
||||||
|
property bool recolorIcons: false
|
||||||
property bool showAudio: true
|
property bool showAudio: true
|
||||||
property bool showBluetooth: false
|
property bool showBluetooth: false
|
||||||
property bool showMicrophone: true
|
property bool showMicrophone: true
|
||||||
property bool showNetwork: false
|
property bool showNetwork: false
|
||||||
|
property bool showOnHover: false
|
||||||
property bool showPower: true
|
property bool showPower: true
|
||||||
property bool showWifi: false
|
property bool showWifi: false
|
||||||
property int trayIconSize: 24
|
property int trayIconSize: 24
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ JsonObject {
|
|||||||
property bool showMemory: true
|
property bool showMemory: true
|
||||||
property bool showNetwork: true
|
property bool showNetwork: true
|
||||||
property bool showStorage: true
|
property bool showStorage: true
|
||||||
|
property bool showVram: true
|
||||||
}
|
}
|
||||||
component Sizes: JsonObject {
|
component Sizes: JsonObject {
|
||||||
readonly property int dateTimeWidth: 110
|
readonly property int dateTimeWidth: 110
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ Item {
|
|||||||
root.panels.osd.hovered = false;
|
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;
|
root.popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -127,7 +128,7 @@ CustomWindow {
|
|||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
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 || (panels.popouts.current as StackView)?.depth > 1))
|
||||||
windows: [root]
|
windows: [root]
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
|
|||||||
@@ -30,18 +30,6 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomRect {
|
|
||||||
// Layout.fillWidth: true
|
|
||||||
// color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
// implicitHeight: resources.implicitHeight
|
|
||||||
// radius: Appearance.rounding.small
|
|
||||||
//
|
|
||||||
// Resources {
|
|
||||||
// id: resources
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
CustomClippingRect {
|
CustomClippingRect {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,5 +9,5 @@ export EGL_PLATFORM=gbm
|
|||||||
if command -v start-hyprland >/dev/null 2>&1; then
|
if command -v start-hyprland >/dev/null 2>&1; then
|
||||||
exec start-hyprland -- -c /etc/zshell-greeter/zshell-hyprland.conf
|
exec start-hyprland -- -c /etc/zshell-greeter/zshell-hyprland.conf
|
||||||
else
|
else
|
||||||
exec Hyprland -c /etc/zshell-greeter/zshell-hyprland.conf
|
exec Hyprland -c /etc/zshell-greeter/zshell-hyprland.lua
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
monitor = ,preferred,auto,1
|
|
||||||
|
|
||||||
env = XDG_SESSION_TYPE,wayland
|
|
||||||
env = QT_QPA_PLATFORM,wayland
|
|
||||||
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
|
|
||||||
|
|
||||||
misc {
|
|
||||||
disable_hyprland_logo = true
|
|
||||||
disable_splash_rendering = true
|
|
||||||
}
|
|
||||||
|
|
||||||
exec = sh -lc 'qs -c zshell-greeter; hyprctl dispatch exit'
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
hl.env("XDG_SESSION_TYPE", "wayland")
|
||||||
|
hl.env("QT_QPA_PLATFORM", "wayland")
|
||||||
|
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
misc = {
|
||||||
|
disable_hyprland_logo = true,
|
||||||
|
disable_splash_rendering = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
hl.exec_cmd("sh -lc 'qs -c zshell-greeter; hyprctl dispatch exit'")
|
||||||
|
end)
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,31 +9,25 @@ import qs.Config
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Private properties
|
|
||||||
property real _downloadSpeed: 0
|
property real _downloadSpeed: 0
|
||||||
property real _downloadTotal: 0
|
property real _downloadTotal: 0
|
||||||
|
|
||||||
// Initial readings for calculating totals
|
|
||||||
property real _initialRxBytes: 0
|
property real _initialRxBytes: 0
|
||||||
property real _initialTxBytes: 0
|
property real _initialTxBytes: 0
|
||||||
property bool _initialized: false
|
property bool _initialized: false
|
||||||
|
|
||||||
// Previous readings for calculating speed
|
|
||||||
property real _prevRxBytes: 0
|
property real _prevRxBytes: 0
|
||||||
property real _prevTimestamp: 0
|
property real _prevTimestamp: 0
|
||||||
property real _prevTxBytes: 0
|
property real _prevTxBytes: 0
|
||||||
property real _uploadSpeed: 0
|
property real _uploadSpeed: 0
|
||||||
property real _uploadTotal: 0
|
property real _uploadTotal: 0
|
||||||
|
|
||||||
// History buffers for sparkline
|
|
||||||
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
||||||
|
|
||||||
// Current speeds in bytes per second
|
|
||||||
readonly property real downloadSpeed: _downloadSpeed
|
readonly property real downloadSpeed: _downloadSpeed
|
||||||
|
|
||||||
// Total bytes transferred since tracking started
|
|
||||||
readonly property real downloadTotal: _downloadTotal
|
readonly property real downloadTotal: _downloadTotal
|
||||||
readonly property int historyLength: 30
|
readonly property int historyLength: Config.dashboard.performance.showVram ? 70 : 30
|
||||||
property int refCount: 0
|
property int refCount: 0
|
||||||
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
||||||
readonly property real uploadSpeed: _uploadSpeed
|
readonly property real uploadSpeed: _uploadSpeed
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,]
|
|
||||||
}
|
|
||||||
+3
-19
@@ -3,12 +3,9 @@ pragma ComponentBehavior: Bound
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Components
|
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
|
||||||
import qs.Modules.SysTray
|
import qs.Modules.SysTray
|
||||||
import qs.Modules.SysTray.Widgets
|
|
||||||
import qs.Modules.Network
|
import qs.Modules.Network
|
||||||
import qs.Modules.Updates
|
import qs.Modules.Updates
|
||||||
|
|
||||||
@@ -26,7 +23,7 @@ RowLayout {
|
|||||||
const ch = childAt(x, height / 2) as WrappedLoader;
|
const ch = childAt(x, height / 2) as WrappedLoader;
|
||||||
|
|
||||||
if (!ch || ch?.id === "spacer") {
|
if (!ch || ch?.id === "spacer") {
|
||||||
if (!popouts.currentName.startsWith("traymenu"))
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover)
|
||||||
popouts.hasCurrent = false;
|
popouts.hasCurrent = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -48,7 +45,7 @@ RowLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!popouts.currentName.startsWith("traymenu"))
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover)
|
||||||
popouts.hasCurrent = false;
|
popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,19 +54,7 @@ RowLayout {
|
|||||||
const item = ch.item;
|
const item = ch.item;
|
||||||
const itemWidth = item.implicitWidth;
|
const itemWidth = item.implicitWidth;
|
||||||
|
|
||||||
if (id === "audio" && Config.bar.popouts.audio) {
|
if (id === "updates") {
|
||||||
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") {
|
|
||||||
popouts.currentName = "updates";
|
popouts.currentName = "updates";
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
popouts.hasCurrent = true;
|
popouts.hasCurrent = true;
|
||||||
@@ -81,7 +66,6 @@ RowLayout {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
// model: Config.bar.entries.filted(n => n.index > 50).sort(n => n.index)
|
|
||||||
model: Config.bar.entries
|
model: Config.bar.entries
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
|
|||||||
@@ -34,21 +34,11 @@ Item {
|
|||||||
implicitHeight: 50
|
implicitHeight: 50
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
MaterialIcon {
|
SearchBar {
|
||||||
id: searchIcon
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: Appearance.padding.large
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: "search"
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomTextField {
|
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: searchIcon.right
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Appearance.spacing.small
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
@@ -170,7 +160,6 @@ Item {
|
|||||||
id: lineText
|
id: lineText
|
||||||
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
font.family: ClipHistory.previewIsCode ? Appearance.font.family.mono : Appearance.font.family.sans
|
|
||||||
height: lineText.paintedHeight + Appearance.padding.extraSmall * 2
|
height: lineText.paintedHeight + Appearance.padding.extraSmall * 2
|
||||||
text: lineRow.modelData.text.trim()
|
text: lineRow.modelData.text.trim()
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
@@ -336,6 +325,8 @@ Item {
|
|||||||
}
|
}
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: ClipHistory.fuzzyQuery(searchField.text)
|
values: ClipHistory.fuzzyQuery(searchField.text)
|
||||||
|
|
||||||
|
onValuesChanged: view.currentIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentItemChanged: {
|
onCurrentItemChanged: {
|
||||||
|
|||||||
@@ -52,11 +52,7 @@ Item {
|
|||||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||||
topPadding: Appearance.padding.larger
|
topPadding: Appearance.padding.larger
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: forceActiveFocus()
|
||||||
console.log(search.color);
|
|
||||||
console.log(search.placeholderTextColor);
|
|
||||||
forceActiveFocus();
|
|
||||||
}
|
|
||||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Item {
|
|||||||
if (visibilities.resources && panels.resourcesWrapper.x + panels.resourcesWrapper.width > root.x)
|
if (visibilities.resources && panels.resourcesWrapper.x + panels.resourcesWrapper.width > root.x)
|
||||||
max -= panels.resources.nonAnimHeight;
|
max -= panels.resources.nonAnimHeight;
|
||||||
if (panels.popouts.hasCurrent)
|
if (panels.popouts.hasCurrent)
|
||||||
if (panels.popouts.current.x + panels.popouts.current.width > root.x && panels.popouts.current.x < root.x + root.width)
|
if (panels.popouts.current?.x + panels.popouts.current?.width > root.x && panels.popouts.current?.x < root.x + root.width)
|
||||||
max -= panels.popouts.nonAnimHeight;
|
max -= panels.popouts.nonAnimHeight;
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ CustomRect {
|
|||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardAccel
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
property: "scale"
|
property: "scale"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 0.7
|
to: 0.7
|
||||||
@@ -141,7 +141,7 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardAccel
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 0
|
to: 0
|
||||||
@@ -166,7 +166,7 @@ CustomRect {
|
|||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardDecel
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
property: "scale"
|
property: "scale"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 1
|
to: 1
|
||||||
@@ -174,7 +174,7 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardDecel
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 1
|
to: 1
|
||||||
@@ -217,14 +217,14 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.large
|
duration: Appearance.anim.durations.large
|
||||||
easing: Appearance.anim.curves.emphasizedAccel
|
easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
|
||||||
from: 1
|
from: 1
|
||||||
to: 0
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.extraLarge
|
duration: Appearance.anim.durations.extraLarge
|
||||||
easing: Appearance.anim.curves.emphasizedDecel
|
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||||
from: 0
|
from: 0
|
||||||
to: 1
|
to: 1
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-37
@@ -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 = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ CustomRect {
|
|||||||
|
|
||||||
readonly property color accent: DynamicColors.palette.m3tertiary
|
readonly property color accent: DynamicColors.palette.m3tertiary
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
||||||
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased * 2
|
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased * 2
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import ZShell.Internal
|
import ZShell.Internal
|
||||||
import qs.Modules.Resources
|
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
@@ -9,8 +8,6 @@ import qs.Config
|
|||||||
CustomRect {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Wrapper wrapper
|
|
||||||
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 220
|
implicitHeight: 220
|
||||||
implicitWidth: 300
|
implicitWidth: 300
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ZShell.Services
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property color accent: DynamicColors.palette.m3tertiary
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
||||||
|
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased
|
||||||
|
radius: Appearance.rounding.medium
|
||||||
|
|
||||||
|
ServiceRef {
|
||||||
|
service: Gpu
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Appearance.spacing.extraSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.leftMargin: -Appearance.padding.extraSmall
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: root.accent
|
||||||
|
fill: 1
|
||||||
|
text: "memory_alt"
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: qsTr("Video memory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CircularProgress {
|
||||||
|
id: circularIndicator
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: Appearance.spacing.large
|
||||||
|
fgColor: root.accent
|
||||||
|
implicitSize: usageColumn.implicitHeight + thickness + Appearance.padding.largeIncreased * 2
|
||||||
|
startAngle: -225
|
||||||
|
sweepAngle: 270
|
||||||
|
value: Gpu.memoryUsed / Gpu.memoryTotal
|
||||||
|
|
||||||
|
Behavior on clampedVal {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: usageColumn
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.verticalCenterOffset: Appearance.padding.extraSmall
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: root.accent
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: Math.round(circularIndicator.value * 100) + "%"
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: qsTr("Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: {
|
||||||
|
const fmt = UsageFmt.formatKib(Gpu.memoryUsed, Gpu.memoryTotal);
|
||||||
|
return `${fmt.value.toFixed(1)} / ${Math.floor(fmt.total)} ${fmt.unit}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ Item {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
visible: storageCard.active || networkCard.active || memoryCard.active
|
visible: storageCard.active || memoryCard.active || vramCard.active || networkCard1.active
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
id: storageCard
|
id: storageCard
|
||||||
@@ -95,15 +95,32 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
id: networkCard
|
id: vramCard
|
||||||
|
|
||||||
active: Config.dashboard.performance.showNetwork
|
active: Config.dashboard.performance.showVram
|
||||||
|
|
||||||
|
sourceComponent: VramCard {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrappedLoader {
|
||||||
|
id: networkCard1
|
||||||
|
|
||||||
|
active: Config.dashboard.performance.showNetwork && !vramCard.active
|
||||||
|
|
||||||
sourceComponent: NetworkCard {
|
sourceComponent: NetworkCard {
|
||||||
wrapper: root.wrapper
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WrappedLoader {
|
||||||
|
id: networkCard2
|
||||||
|
|
||||||
|
active: Config.dashboard.performance.showNetwork && vramCard.active
|
||||||
|
|
||||||
|
sourceComponent: NetworkCard {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
|
|||||||
@@ -3,12 +3,64 @@ import qs.Components
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
property bool first
|
property bool first
|
||||||
property bool last
|
property bool last
|
||||||
|
property string settingAnchor
|
||||||
|
|
||||||
|
function flashHighlight(): void {
|
||||||
|
flash.restart();
|
||||||
|
}
|
||||||
|
|
||||||
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: highlight
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
bottomLeftRadius: parent.bottomLeftRadius
|
||||||
|
bottomRightRadius: parent.bottomRightRadius
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
opacity: 0
|
||||||
|
radius: parent.radius
|
||||||
|
topLeftRadius: parent.topLeftRadius
|
||||||
|
topRightRadius: parent.topRightRadius
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: flash
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.08
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.extraLarge
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import qs.Config
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool animateScroll: false
|
||||||
readonly property int cappedWidth: Math.min(800, width)
|
readonly property int cappedWidth: Math.min(800, width)
|
||||||
default property Item contentChild
|
default property Item contentChild
|
||||||
readonly property alias flickable: flickable
|
readonly property alias flickable: flickable
|
||||||
@@ -16,11 +17,100 @@ ColumnLayout {
|
|||||||
required property SettingsState sState
|
required property SettingsState sState
|
||||||
required property string title
|
required property string title
|
||||||
|
|
||||||
|
function applySearchAnchor(): void {
|
||||||
|
if (!sState.searchAnchor)
|
||||||
|
return;
|
||||||
|
scrollRetry.tries = 0;
|
||||||
|
scrollRetry.lastHeight = -1;
|
||||||
|
scrollRetry.stableFrames = 0;
|
||||||
|
scrollRetry.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findAnchor(item: Item, anchor: string): Item {
|
||||||
|
if (!item)
|
||||||
|
return null;
|
||||||
|
if (item.settingAnchor !== undefined && item.settingAnchor === anchor)
|
||||||
|
return item;
|
||||||
|
const kids = item.children;
|
||||||
|
for (let i = 0; i < kids.length; i++) {
|
||||||
|
const found = findAnchor(kids[i], anchor);
|
||||||
|
if (found)
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightAnchor(anchor: string): void {
|
||||||
|
const row = findAnchor(contentChild, anchor);
|
||||||
|
if (row && row.flashHighlight !== undefined)
|
||||||
|
row.flashHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToAnchor(anchor: string): bool {
|
||||||
|
if (!anchor || !contentChild)
|
||||||
|
return false;
|
||||||
|
const row = findAnchor(contentChild, anchor);
|
||||||
|
if (!row)
|
||||||
|
return false;
|
||||||
|
const pos = row.mapToItem(flickable.contentItem, 0, 0);
|
||||||
|
const inset = flickable.height * flickable.fadeAmount + Appearance.padding.large;
|
||||||
|
const minY = -flickable.topMargin;
|
||||||
|
const maxY = Math.max(minY, flickable.contentHeight + flickable.bottomMargin - flickable.height);
|
||||||
|
const target = Math.max(minY, Math.min(pos.y - inset, maxY));
|
||||||
|
root.animateScroll = true;
|
||||||
|
flickable.contentY = target;
|
||||||
|
Qt.callLater(() => root.animateScroll = false);
|
||||||
|
if (row.flashHighlight !== undefined)
|
||||||
|
row.flashHighlight();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
spacing: Appearance.spacing.large
|
spacing: Appearance.spacing.large
|
||||||
|
|
||||||
MouseArea { // Prevent clicks from reaching flickable
|
Component.onCompleted: applySearchAnchor()
|
||||||
Layout.bottomMargin: -flickable.topMargin // Extra height to block clicks on flickable top margin
|
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: scrollRetry
|
||||||
|
|
||||||
|
property real lastHeight: -1
|
||||||
|
property int stableFrames: 0
|
||||||
|
property int tries: 0
|
||||||
|
|
||||||
|
interval: 16
|
||||||
|
repeat: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
const h = flickable.contentHeight;
|
||||||
|
if (h === lastHeight && h > flickable.height)
|
||||||
|
stableFrames++;
|
||||||
|
else
|
||||||
|
stableFrames = 0;
|
||||||
|
lastHeight = h;
|
||||||
|
|
||||||
|
const ready = stableFrames >= 3 || tries >= 30;
|
||||||
|
if (ready) {
|
||||||
|
if (root.scrollToAnchor(root.sState.searchAnchor))
|
||||||
|
root.sState.searchAnchor = "";
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onHighlightSetting(anchor: string): void {
|
||||||
|
root.highlightAnchor(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearchAnchorChanged(): void {
|
||||||
|
root.applySearchAnchor();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root.sState
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
Layout.bottomMargin: -flickable.topMargin
|
||||||
implicitHeight: header.implicitHeight - Layout.bottomMargin
|
implicitHeight: header.implicitHeight - Layout.bottomMargin
|
||||||
implicitWidth: header.implicitWidth
|
implicitWidth: header.implicitWidth
|
||||||
z: 1
|
z: 1
|
||||||
@@ -69,6 +159,14 @@ ColumnLayout {
|
|||||||
fadeAmount: 0.1
|
fadeAmount: 0.1
|
||||||
topMargin: Appearance.padding.large
|
topMargin: Appearance.padding.large
|
||||||
|
|
||||||
|
Behavior on contentY {
|
||||||
|
enabled: root.animateScroll
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
type: Anim.DefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onTapped: flickable.focus = true
|
onTapped: flickable.focus = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,13 @@ CustomSwitch {
|
|||||||
readonly property alias bg: bg
|
readonly property alias bg: bg
|
||||||
property alias first: bg.first
|
property alias first: bg.first
|
||||||
property alias last: bg.last
|
property alias last: bg.last
|
||||||
|
property string settingAnchor
|
||||||
property string subtext
|
property string subtext
|
||||||
|
|
||||||
|
function flashHighlight(): void {
|
||||||
|
bg.flashHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
cLayer: 2
|
cLayer: 2
|
||||||
horizontalPadding: Appearance.padding.largeIncreased
|
horizontalPadding: Appearance.padding.largeIncreased
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ ColumnLayout {
|
|||||||
target: root.sState
|
target: root.sState
|
||||||
value: searchField.text.length > 0
|
value: searchField.text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
property: "searchText"
|
||||||
|
target: root.sState
|
||||||
|
value: searchField.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NavLocations {
|
NavLocations {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
@@ -9,7 +10,33 @@ import qs.Modules.Settings
|
|||||||
VerticalFadeFlickable {
|
VerticalFadeFlickable {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property var groups: {
|
||||||
|
const out = [];
|
||||||
|
const byPage = ({});
|
||||||
|
for (const e of results) {
|
||||||
|
const key = e.pageIdx;
|
||||||
|
if (byPage[key] === undefined) {
|
||||||
|
byPage[key] = {
|
||||||
|
"pageIdx": e.pageIdx,
|
||||||
|
"page": e.crumbLabels[0],
|
||||||
|
"icon": e.crumbIcons[0],
|
||||||
|
"entries": []
|
||||||
|
};
|
||||||
|
out.push(byPage[key]);
|
||||||
|
}
|
||||||
|
byPage[key].entries.push(e);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
readonly property var results: {
|
||||||
|
if (!searching)
|
||||||
|
return [];
|
||||||
|
const all = SettingsSearcher.query(search);
|
||||||
|
return all;
|
||||||
|
}
|
||||||
required property SettingsState sState
|
required property SettingsState sState
|
||||||
|
readonly property string search: sState.searchText
|
||||||
|
readonly property bool searching: search.length > 0
|
||||||
|
|
||||||
bottomMargin: Appearance.padding.large
|
bottomMargin: Appearance.padding.large
|
||||||
contentHeight: content.implicitHeight
|
contentHeight: content.implicitHeight
|
||||||
@@ -30,14 +57,14 @@ VerticalFadeFlickable {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: list
|
id: list
|
||||||
|
|
||||||
model: PageRegistry.pages
|
model: root.searching ? [] : PageRegistry.pages
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: item
|
id: item
|
||||||
|
|
||||||
required property int index
|
required property int index
|
||||||
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1].category !== modelData.category
|
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1]?.category !== modelData.category
|
||||||
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1].category !== modelData.category
|
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1]?.category !== modelData.category
|
||||||
readonly property bool isCurrentPage: index === root.sState.currentPageIdx
|
readonly property bool isCurrentPage: index === root.sState.currentPageIdx
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
@@ -120,6 +147,166 @@ VerticalFadeFlickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: resultList
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cacheBuffer: 10000
|
||||||
|
implicitHeight: contentHeight
|
||||||
|
interactive: false
|
||||||
|
spacing: Appearance.padding.large
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: group
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
width: resultList.width
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Appearance.padding.small
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: group.modelData.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: group.modelData.page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.extraSmall / 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: group.modelData.entries
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: result
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
readonly property bool isFirst: index === 0
|
||||||
|
readonly property bool isLast: index === group.modelData.entries.length - 1
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
bottomLeftRadius: layer.pressed ? Appearance.rounding.medium : isLast ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
bottomRightRadius: layer.pressed ? Appearance.rounding.medium : isLast ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||||
|
implicitHeight: {
|
||||||
|
const h = resultLayout.implicitHeight + resultLayout.anchors.margins * 2;
|
||||||
|
return h % 2 === 0 ? h : h + 1;
|
||||||
|
}
|
||||||
|
topLeftRadius: layer.pressed ? Appearance.rounding.medium : isFirst ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
topRightRadius: layer.pressed ? Appearance.rounding.medium : isFirst ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
|
||||||
|
RadiusBehavior on bottomLeftRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on bottomRightRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on topLeftRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on topRightRadius {
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: resultLayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.large
|
||||||
|
anchors.rightMargin: result.modelData.togglePath ? toggle.width + Appearance.padding.large * 2 : Appearance.padding.large
|
||||||
|
spacing: Appearance.spacing.small / 2
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
text: {
|
||||||
|
const labels = result.modelData.crumbLabels.slice(1);
|
||||||
|
const section = result.modelData.section;
|
||||||
|
const parts = section && section !== labels[labels.length - 1] ? labels.concat(section) : labels;
|
||||||
|
return parts.join(" \u203a ");
|
||||||
|
}
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3onSurface
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.medium
|
||||||
|
text: SettingsSearcher.highlight(result.modelData.title, root.search, DynamicColors.palette.m3primary)
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3outline
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
text: SettingsSearcher.highlight(result.modelData.subtext, root.search, DynamicColors.palette.m3primary)
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
visible: result.modelData.subtext.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: layer
|
||||||
|
|
||||||
|
z: 1
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.sState.jumpToSetting(result.modelData.pageIdx, result.modelData.subPath, result.modelData.anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
id: toggle
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Appearance.padding.large
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
cLayer: 3
|
||||||
|
checked: result.modelData.toggleValue
|
||||||
|
scale: 0.85
|
||||||
|
transformOrigin: Item.Right
|
||||||
|
visible: result.modelData.togglePath
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
onToggled: result.modelData.setToggle(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model: ScriptModel {
|
||||||
|
objectProp: "pageIdx"
|
||||||
|
values: root.groups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Appearance.padding.large
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.medium
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("No matching settings")
|
||||||
|
visible: root.searching && root.results.length === 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component RadiusBehavior: Behavior {
|
component RadiusBehavior: Behavior {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ PageBase {
|
|||||||
DefaultRow {
|
DefaultRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "terminal"
|
icon: "terminal"
|
||||||
|
settingAnchor: "apps-default-terminal"
|
||||||
status: Config.general.apps.terminal.join(" ")
|
status: Config.general.apps.terminal.join(" ")
|
||||||
text: qsTr("Terminal")
|
text: qsTr("Terminal")
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ PageBase {
|
|||||||
|
|
||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "volume_up"
|
icon: "volume_up"
|
||||||
|
settingAnchor: "apps-default-audio"
|
||||||
status: Config.general.apps.audio.join(" ")
|
status: Config.general.apps.audio.join(" ")
|
||||||
text: qsTr("Audio")
|
text: qsTr("Audio")
|
||||||
|
|
||||||
@@ -46,6 +48,7 @@ PageBase {
|
|||||||
|
|
||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "play_circle"
|
icon: "play_circle"
|
||||||
|
settingAnchor: "apps-default-playback"
|
||||||
status: Config.general.apps.playback.join(" ")
|
status: Config.general.apps.playback.join(" ")
|
||||||
text: qsTr("Media playback")
|
text: qsTr("Media playback")
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ PageBase {
|
|||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "folder"
|
icon: "folder"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "apps-default-file-manager"
|
||||||
status: Config.general.apps.explorer.join(" ")
|
status: Config.general.apps.explorer.join(" ")
|
||||||
text: qsTr("File manager")
|
text: qsTr("File manager")
|
||||||
|
|
||||||
@@ -70,6 +74,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: "apps"
|
icon: "apps"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "apps-all-apps"
|
||||||
status: qsTr("Browse installed apps, set favorites and hidden")
|
status: qsTr("Browse installed apps, set favorites and hidden")
|
||||||
text: qsTr("All apps")
|
text: qsTr("All apps")
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
||||||
label: qsTr("Output")
|
label: qsTr("Output")
|
||||||
|
settingAnchor: "audio-output"
|
||||||
value: Audio.volume
|
value: Audio.volume
|
||||||
valueLabel: Math.round(value * 100) + "%"
|
valueLabel: Math.round(value * 100) + "%"
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
icon: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
||||||
label: qsTr("Input")
|
label: qsTr("Input")
|
||||||
|
settingAnchor: "audio-input"
|
||||||
value: Audio.sourceVolume
|
value: Audio.sourceVolume
|
||||||
valueLabel: Math.round(value * 100) + "%"
|
valueLabel: Math.round(value * 100) + "%"
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
menuItems: root.clockFormats
|
menuItems: root.clockFormats
|
||||||
|
settingAnchor: "bar-clock-format"
|
||||||
subtext: qsTr("Change how time is displayed in the widget")
|
subtext: qsTr("Change how time is displayed in the widget")
|
||||||
text: qsTr("Time format")
|
text: qsTr("Time format")
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showAudio
|
checked: Config.bar.tray.showAudio
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "bar-status-speakers"
|
||||||
text: qsTr("Speakers")
|
text: qsTr("Speakers")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showAudio = checked
|
onToggled: Config.bar.tray.showAudio = checked
|
||||||
@@ -32,6 +33,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showMicrophone
|
checked: Config.bar.tray.showMicrophone
|
||||||
|
settingAnchor: "bar-status-mic"
|
||||||
text: qsTr("Microphone")
|
text: qsTr("Microphone")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showMicrophone = checked
|
onToggled: Config.bar.tray.showMicrophone = checked
|
||||||
@@ -62,6 +64,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showPower
|
checked: Config.bar.tray.showPower
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-status-power"
|
||||||
text: qsTr("Battery")
|
text: qsTr("Battery")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showPower = checked
|
onToggled: Config.bar.tray.showPower = checked
|
||||||
@@ -75,6 +78,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.popouts.audio
|
checked: Config.bar.popouts.audio
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "bar-status-audio-popout"
|
||||||
subtext: qsTr("Show a details popout when hovering the audio icons")
|
subtext: qsTr("Show a details popout when hovering the audio icons")
|
||||||
text: qsTr("Audio popout on hover")
|
text: qsTr("Audio popout on hover")
|
||||||
|
|
||||||
@@ -84,6 +88,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.popouts.upower
|
checked: Config.bar.popouts.upower
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-status-power-popout"
|
||||||
subtext: qsTr("Show a details popout when hovering the power icon")
|
subtext: qsTr("Show a details popout when hovering the power icon")
|
||||||
text: qsTr("Power popout on hover")
|
text: qsTr("Power popout on hover")
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 12
|
from: 12
|
||||||
last: true
|
settingAnchor: "bar-tray-iconsize"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Icon size")
|
text: qsTr("Icon size")
|
||||||
to: 24
|
to: 24
|
||||||
@@ -27,5 +27,24 @@ PageBase {
|
|||||||
|
|
||||||
onMoved: value => Config.bar.tray.trayIconSize = value
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ PageBase {
|
|||||||
checked: Config.bar.autoHide
|
checked: Config.bar.autoHide
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-autohide"
|
||||||
subtext: qsTr("Hide the bar, reveal on hover")
|
subtext: qsTr("Hide the bar, reveal on hover")
|
||||||
text: qsTr("Auto hide")
|
text: qsTr("Auto hide")
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "widgets"
|
icon: "widgets"
|
||||||
|
settingAnchor: "bar-tray"
|
||||||
status: qsTr("System tray icons")
|
status: qsTr("System tray icons")
|
||||||
text: qsTr("Tray")
|
text: qsTr("Tray")
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "signal_cellular_alt"
|
icon: "signal_cellular_alt"
|
||||||
|
settingAnchor: "bar-statusicons"
|
||||||
status: qsTr("Visible indicators")
|
status: qsTr("Visible indicators")
|
||||||
text: qsTr("Status icons")
|
text: qsTr("Status icons")
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
icon: "schedule"
|
icon: "schedule"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-clock"
|
||||||
status: qsTr("Date, icon, background")
|
status: qsTr("Date, icon, background")
|
||||||
text: qsTr("Clock")
|
text: qsTr("Clock")
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.dashboard.enabled
|
checked: Config.dashboard.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "dashboard-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.dashboard.enabled = checked
|
onToggled: Config.dashboard.enabled = checked
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.launcher.enabled
|
checked: Config.launcher.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.launcher.enabled = checked
|
onToggled: Config.launcher.enabled = checked
|
||||||
@@ -41,6 +42,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "launcher-max-items-shown"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Max items shown")
|
text: qsTr("Max items shown")
|
||||||
to: 20
|
to: 20
|
||||||
@@ -51,6 +53,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "launcher-max-wallpapers"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Max wallpapers")
|
text: qsTr("Max wallpapers")
|
||||||
to: 30
|
to: 30
|
||||||
@@ -62,6 +65,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-drag-threshold"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Pixels dragged before the launcher opens")
|
subtext: qsTr("Pixels dragged before the launcher opens")
|
||||||
text: qsTr("Drag threshold")
|
text: qsTr("Drag threshold")
|
||||||
@@ -80,6 +84,7 @@ PageBase {
|
|||||||
checked: Config.launcher.enableDangerousActions
|
checked: Config.launcher.enableDangerousActions
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-enable-dangerous-actions"
|
||||||
subtext: qsTr("Allow actions that shut down or log out")
|
subtext: qsTr("Allow actions that shut down or log out")
|
||||||
text: qsTr("Enable dangerous actions")
|
text: qsTr("Enable dangerous actions")
|
||||||
|
|
||||||
@@ -94,6 +99,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.apps
|
checked: Config.launcher.useFuzzy.apps
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "launcher-apps"
|
||||||
text: qsTr("Apps")
|
text: qsTr("Apps")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.apps = checked
|
onToggled: Config.launcher.useFuzzy.apps = checked
|
||||||
@@ -101,20 +107,15 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.actions
|
checked: Config.launcher.useFuzzy.actions
|
||||||
|
settingAnchor: "launcher-actions"
|
||||||
text: qsTr("Actions")
|
text: qsTr("Actions")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.actions = checked
|
onToggled: Config.launcher.useFuzzy.actions = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleRow {
|
|
||||||
checked: Config.launcher.useFuzzy.schemes
|
|
||||||
text: qsTr("Schemes")
|
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.schemes = checked
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.variants
|
checked: Config.launcher.useFuzzy.variants
|
||||||
|
settingAnchor: "launcher-variants"
|
||||||
text: qsTr("Variants")
|
text: qsTr("Variants")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.variants = checked
|
onToggled: Config.launcher.useFuzzy.variants = checked
|
||||||
@@ -123,6 +124,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.wallpapers
|
checked: Config.launcher.useFuzzy.wallpapers
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-wallpapers"
|
||||||
text: qsTr("Wallpapers")
|
text: qsTr("Wallpapers")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.wallpapers = checked
|
onToggled: Config.launcher.useFuzzy.wallpapers = checked
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.dashboard.performance.enabled
|
checked: Config.dashboard.performance.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "resources-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.enabled = checked
|
onToggled: Config.dashboard.performance.enabled = checked
|
||||||
@@ -41,6 +42,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showBattery
|
checked: Config.dashboard.performance.showBattery
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "resources-battery"
|
||||||
text: qsTr("Battery")
|
text: qsTr("Battery")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showBattery = checked
|
onToggled: Config.dashboard.performance.showBattery = checked
|
||||||
@@ -48,13 +50,23 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showGpu
|
checked: Config.dashboard.performance.showGpu
|
||||||
|
settingAnchor: "resources-gpu"
|
||||||
text: qsTr("GPU")
|
text: qsTr("GPU")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showGpu = checked
|
onToggled: Config.dashboard.performance.showGpu = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.dashboard.performance.showVram
|
||||||
|
settingAnchor: "resources-vram"
|
||||||
|
text: qsTr("Video memory")
|
||||||
|
|
||||||
|
onToggled: Config.dashboard.performance.showVram = checked
|
||||||
|
}
|
||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showCpu
|
checked: Config.dashboard.performance.showCpu
|
||||||
|
settingAnchor: "resources-cpu"
|
||||||
text: qsTr("CPU")
|
text: qsTr("CPU")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showCpu = checked
|
onToggled: Config.dashboard.performance.showCpu = checked
|
||||||
@@ -62,6 +74,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showMemory
|
checked: Config.dashboard.performance.showMemory
|
||||||
|
settingAnchor: "resources-memory"
|
||||||
text: qsTr("Memory")
|
text: qsTr("Memory")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showMemory = checked
|
onToggled: Config.dashboard.performance.showMemory = checked
|
||||||
@@ -69,6 +82,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showStorage
|
checked: Config.dashboard.performance.showStorage
|
||||||
|
settingAnchor: "resources-storage"
|
||||||
text: qsTr("Storage")
|
text: qsTr("Storage")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showStorage = checked
|
onToggled: Config.dashboard.performance.showStorage = checked
|
||||||
@@ -77,6 +91,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showNetwork
|
checked: Config.dashboard.performance.showNetwork
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "resources-network"
|
||||||
text: qsTr("Network")
|
text: qsTr("Network")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showNetwork = checked
|
onToggled: Config.dashboard.performance.showNetwork = checked
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.sidebar.enabled
|
checked: Config.sidebar.enabled
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "sidebar-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.sidebar.enabled = checked
|
onToggled: Config.sidebar.enabled = checked
|
||||||
@@ -34,6 +35,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "sidebar-drag-threshold"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Pixels dragged before the sidebar opens")
|
subtext: qsTr("Pixels dragged before the sidebar opens")
|
||||||
text: qsTr("Drag threshold")
|
text: qsTr("Drag threshold")
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "dock_to_bottom"
|
icon: "dock_to_bottom"
|
||||||
|
settingAnchor: "panels-bar"
|
||||||
status: !Config.bar.autoHide ? qsTr("Always visible") : qsTr("Reveal on hover")
|
status: !Config.bar.autoHide ? qsTr("Always visible") : qsTr("Reveal on hover")
|
||||||
text: qsTr("Bar")
|
text: qsTr("Bar")
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "dashboard"
|
icon: "dashboard"
|
||||||
|
settingAnchor: "panels-dashboard"
|
||||||
status: Config.dashboard.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.dashboard.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Dashboard")
|
text: qsTr("Dashboard")
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "insert_chart"
|
icon: "insert_chart"
|
||||||
|
settingAnchor: "panels-resources"
|
||||||
status: Config.dashboard.performance.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.dashboard.performance.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Resources")
|
text: qsTr("Resources")
|
||||||
|
|
||||||
@@ -40,6 +43,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "apps"
|
icon: "apps"
|
||||||
|
settingAnchor: "panels-launcher"
|
||||||
status: Config.launcher.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.launcher.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Launcher")
|
text: qsTr("Launcher")
|
||||||
|
|
||||||
@@ -49,6 +53,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
icon: "dock_to_right"
|
icon: "dock_to_right"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "panels-sidebar"
|
||||||
status: Config.sidebar.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.sidebar.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Sidebar")
|
text: qsTr("Sidebar")
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.screenshot.enable_pp
|
checked: Config.screenshot.enable_pp
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable"
|
||||||
text: qsTr("Enable effects")
|
text: qsTr("Enable effects")
|
||||||
|
|
||||||
onToggled: Config.screenshot.enable_pp = checked
|
onToggled: Config.screenshot.enable_pp = checked
|
||||||
@@ -37,6 +38,7 @@ PageBase {
|
|||||||
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
||||||
enabled: Config.screenshot.enable_pp
|
enabled: Config.screenshot.enable_pp
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-mode"
|
||||||
subtext: qsTr("Automatic or manual effect values")
|
subtext: qsTr("Automatic or manual effect values")
|
||||||
text: qsTr("Effects mode")
|
text: qsTr("Effects mode")
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ PageBase {
|
|||||||
checked: Config.screenshot.rounding
|
checked: Config.screenshot.rounding
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable-rounded-corners"
|
||||||
text: qsTr("Enable rounded corners")
|
text: qsTr("Enable rounded corners")
|
||||||
|
|
||||||
onToggled: Config.screenshot.rounding = checked
|
onToggled: Config.screenshot.rounding = checked
|
||||||
@@ -76,6 +79,7 @@ PageBase {
|
|||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-corner-radius"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Corner radius")
|
text: qsTr("Corner radius")
|
||||||
to: 50
|
to: 50
|
||||||
@@ -95,6 +99,7 @@ PageBase {
|
|||||||
checked: Config.screenshot.shadow
|
checked: Config.screenshot.shadow
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable-shadow"
|
||||||
text: qsTr("Enable shadow")
|
text: qsTr("Enable shadow")
|
||||||
|
|
||||||
onToggled: Config.screenshot.shadow = checked
|
onToggled: Config.screenshot.shadow = checked
|
||||||
@@ -103,6 +108,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: 0
|
from: 0
|
||||||
|
settingAnchor: "screenshot-shadow-blur-amount"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Shadow blur amount")
|
text: qsTr("Shadow blur amount")
|
||||||
to: 100
|
to: 100
|
||||||
@@ -117,6 +123,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: -100
|
from: -100
|
||||||
|
settingAnchor: "screenshot-shadow-horizontal-offset"
|
||||||
stepSize: 10
|
stepSize: 10
|
||||||
text: qsTr("Shadow horizontal offset")
|
text: qsTr("Shadow horizontal offset")
|
||||||
to: 100
|
to: 100
|
||||||
@@ -132,6 +139,7 @@ PageBase {
|
|||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: -100
|
from: -100
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-shadow-vertical-offset"
|
||||||
stepSize: 10
|
stepSize: 10
|
||||||
text: qsTr("Shadow vertical offset")
|
text: qsTr("Shadow vertical offset")
|
||||||
to: 100
|
to: 100
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.showInFullscreen
|
checked: Config.notifs.showInFullscreen
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "notif-show-in-fullscreen"
|
||||||
subtext: qsTr("Whether notifications appear over fullscreen apps")
|
subtext: qsTr("Whether notifications appear over fullscreen apps")
|
||||||
text: qsTr("Show in fullscreen")
|
text: qsTr("Show in fullscreen")
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.expire
|
checked: Config.notifs.expire
|
||||||
|
settingAnchor: "notif-expire-automatically"
|
||||||
subtext: qsTr("Dismiss notifications after their timeout")
|
subtext: qsTr("Dismiss notifications after their timeout")
|
||||||
text: qsTr("Expire automatically")
|
text: qsTr("Expire automatically")
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.openExpanded
|
checked: Config.notifs.openExpanded
|
||||||
|
settingAnchor: "notif-open-expanded"
|
||||||
subtext: qsTr("Show notifications expanded by default")
|
subtext: qsTr("Show notifications expanded by default")
|
||||||
text: qsTr("Open expanded")
|
text: qsTr("Open expanded")
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1000
|
from: 1000
|
||||||
|
settingAnchor: "notif-default-timeout"
|
||||||
stepSize: 500
|
stepSize: 500
|
||||||
subtext: qsTr("Time before a notification dismisses (ms)")
|
subtext: qsTr("Time before a notification dismisses (ms)")
|
||||||
text: qsTr("Default timeout")
|
text: qsTr("Default timeout")
|
||||||
@@ -61,6 +65,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "notif-group-preview-count"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Notifications shown per group before collapsing")
|
subtext: qsTr("Notifications shown per group before collapsing")
|
||||||
text: qsTr("Group preview count")
|
text: qsTr("Group preview count")
|
||||||
@@ -78,6 +83,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "notif-visible-toasts"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Maximum number of toasts shown at once")
|
subtext: qsTr("Maximum number of toasts shown at once")
|
||||||
text: qsTr("Visible toasts")
|
text: qsTr("Visible toasts")
|
||||||
@@ -89,6 +95,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.chargingChanged
|
checked: Config.utilities.toasts.chargingChanged
|
||||||
|
settingAnchor: "notif-charging-changes"
|
||||||
text: qsTr("Charging changes")
|
text: qsTr("Charging changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.chargingChanged = checked
|
onToggled: Config.utilities.toasts.chargingChanged = checked
|
||||||
@@ -96,6 +103,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.gameModeChanged
|
checked: Config.utilities.toasts.gameModeChanged
|
||||||
|
settingAnchor: "notif-game-mode-changes"
|
||||||
text: qsTr("Game mode changes")
|
text: qsTr("Game mode changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.gameModeChanged = checked
|
onToggled: Config.utilities.toasts.gameModeChanged = checked
|
||||||
@@ -110,6 +118,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.audioOutputChanged
|
checked: Config.utilities.toasts.audioOutputChanged
|
||||||
|
settingAnchor: "notif-audio-output-changes"
|
||||||
text: qsTr("Audio output changes")
|
text: qsTr("Audio output changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.audioOutputChanged = checked
|
onToggled: Config.utilities.toasts.audioOutputChanged = checked
|
||||||
@@ -118,6 +127,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.audioInputChanged
|
checked: Config.utilities.toasts.audioInputChanged
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "notif-audio-input-changes"
|
||||||
text: qsTr("Audio input changes")
|
text: qsTr("Audio input changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.audioInputChanged = checked
|
onToggled: Config.utilities.toasts.audioInputChanged = checked
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: "notifications"
|
icon: "notifications"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-notifications"
|
||||||
status: qsTr("Notifications, toasts, timeouts")
|
status: qsTr("Notifications, toasts, timeouts")
|
||||||
text: qsTr("Notifications")
|
text: qsTr("Notifications")
|
||||||
|
|
||||||
@@ -85,6 +86,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 100
|
from: 100
|
||||||
|
settingAnchor: "services-media-refresh"
|
||||||
stepSize: 50
|
stepSize: 50
|
||||||
subtext: qsTr("How often the media position updates (ms)")
|
subtext: qsTr("How often the media position updates (ms)")
|
||||||
text: qsTr("Media refresh")
|
text: qsTr("Media refresh")
|
||||||
@@ -97,6 +99,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0.5
|
from: 0.5
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-system-stats-refresh"
|
||||||
stepSize: 0.5
|
stepSize: 0.5
|
||||||
subtext: qsTr("CPU, memory and GPU update interval (seconds)")
|
subtext: qsTr("CPU, memory and GPU update interval (seconds)")
|
||||||
text: qsTr("System stats refresh")
|
text: qsTr("System stats refresh")
|
||||||
@@ -118,6 +121,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
menuItems: playerVariants.instances
|
menuItems: playerVariants.instances
|
||||||
|
settingAnchor: "services-default-player"
|
||||||
subtext: qsTr("Preferred media player when several are open")
|
subtext: qsTr("Preferred media player when several are open")
|
||||||
text: qsTr("Default player")
|
text: qsTr("Default player")
|
||||||
|
|
||||||
@@ -132,6 +136,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-volume-step"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Amount the volume changes per input (%)")
|
subtext: qsTr("Amount the volume changes per input (%)")
|
||||||
text: qsTr("Volume step")
|
text: qsTr("Volume step")
|
||||||
@@ -143,6 +148,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-brightness-step"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Amount the brightness changes per input (%)")
|
subtext: qsTr("Amount the brightness changes per input (%)")
|
||||||
text: qsTr("Brightness step")
|
text: qsTr("Brightness step")
|
||||||
@@ -154,6 +160,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-brightness-minimum"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Lowest allowed brightness (%)")
|
subtext: qsTr("Lowest allowed brightness (%)")
|
||||||
text: qsTr("Brightness minimum")
|
text: qsTr("Brightness minimum")
|
||||||
@@ -166,6 +173,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 50
|
from: 50
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-max-volume"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Upper limit for output volume (%)")
|
subtext: qsTr("Upper limit for output volume (%)")
|
||||||
text: qsTr("Max volume")
|
text: qsTr("Max volume")
|
||||||
@@ -183,6 +191,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 10
|
from: 10
|
||||||
|
settingAnchor: "services-visualizer-resolution"
|
||||||
stepSize: 2
|
stepSize: 2
|
||||||
subtext: qsTr("Resolution of the audio visualizer")
|
subtext: qsTr("Resolution of the audio visualizer")
|
||||||
text: qsTr("Visualizer resolution")
|
text: qsTr("Visualizer resolution")
|
||||||
@@ -194,6 +203,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.general.color.smart
|
checked: Config.general.color.smart
|
||||||
|
settingAnchor: "services-smart-color-scheme"
|
||||||
subtext: qsTr("Derive theme mode from the wallpaper")
|
subtext: qsTr("Derive theme mode from the wallpaper")
|
||||||
text: qsTr("Smart color scheme")
|
text: qsTr("Smart color scheme")
|
||||||
|
|
||||||
@@ -205,6 +215,7 @@ PageBase {
|
|||||||
last: true
|
last: true
|
||||||
menuItems: root.gpuItems
|
menuItems: root.gpuItems
|
||||||
menuOnTop: true
|
menuOnTop: true
|
||||||
|
settingAnchor: "services-gpu"
|
||||||
subtext: Gpu.name ? qsTr("Monitoring: %1").arg(Gpu.name) : qsTr("Override for GPU type")
|
subtext: Gpu.name ? qsTr("Monitoring: %1").arg(Gpu.name) : qsTr("Override for GPU type")
|
||||||
text: qsTr("GPU")
|
text: qsTr("GPU")
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.background.enabled
|
checked: Config.background.enabled
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "style-display-wallpaper"
|
||||||
text: qsTr("Display wallpaper")
|
text: qsTr("Display wallpaper")
|
||||||
|
|
||||||
onToggled: Config.background.enabled = checked
|
onToggled: Config.background.enabled = checked
|
||||||
@@ -57,6 +58,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: DynamicColors.transparency.enabled
|
checked: DynamicColors.transparency.enabled
|
||||||
|
settingAnchor: "style-transparency"
|
||||||
subtext: qsTr("Base %1, layers %2").arg(DynamicColors.transparency.base).arg(DynamicColors.transparency.layers)
|
subtext: qsTr("Base %1, layers %2").arg(DynamicColors.transparency.base).arg(DynamicColors.transparency.layers)
|
||||||
text: qsTr("Transparency")
|
text: qsTr("Transparency")
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ PageBase {
|
|||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: !DynamicColors.light
|
checked: !DynamicColors.light
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "style-dark-theme"
|
||||||
text: qsTr("Dark theme")
|
text: qsTr("Dark theme")
|
||||||
|
|
||||||
onToggled: DynamicColors.setMode(checked ? "dark" : "light")
|
onToggled: DynamicColors.setMode(checked ? "dark" : "light")
|
||||||
@@ -88,6 +91,7 @@ PageBase {
|
|||||||
|
|
||||||
checked: Config.general.color.scheduleDark
|
checked: Config.general.color.scheduleDark
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "style-schedule-dark-mode"
|
||||||
subtext: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
subtext: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
||||||
text: qsTr("Schedule dark mode")
|
text: qsTr("Schedule dark mode")
|
||||||
|
|
||||||
@@ -129,6 +133,7 @@ PageBase {
|
|||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: Config.general.color.scheduleHyprsunset
|
checked: Config.general.color.scheduleHyprsunset
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "style-schedule-hyprsunset"
|
||||||
subtext: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
subtext: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
||||||
text: qsTr("Schedule hyprsunset")
|
text: qsTr("Schedule hyprsunset")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import "../../scripts/fzf.js" as Fzf
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import ZShell
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var fzfFinder: null
|
||||||
|
property var inverted: ({})
|
||||||
|
property var ranking: ({})
|
||||||
|
|
||||||
|
function highlight(text: string, search: string, colour: color): string {
|
||||||
|
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
const tokens = tokenize(search);
|
||||||
|
if (tokens.length === 0)
|
||||||
|
return escaped;
|
||||||
|
const escapedTokens = tokens.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||||
|
const pattern = new RegExp("\\b(" + escapedTokens.join("|") + ")", "gi");
|
||||||
|
return escaped.replace(pattern, `<font color="${colour}">$1</font>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup(token: string): var {
|
||||||
|
const result = ({});
|
||||||
|
const exact = root.inverted[token] !== undefined;
|
||||||
|
const keys = exact ? [token] : Object.keys(root.inverted).filter(k => k.startsWith(token));
|
||||||
|
for (const key of keys) {
|
||||||
|
const rank = root.ranking[key] ?? ({});
|
||||||
|
for (const id of root.inverted[key]) {
|
||||||
|
const w = rank[id] ?? 0.1;
|
||||||
|
if (result[id] === undefined || w > result[id])
|
||||||
|
result[id] = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function query(search: string): list<QtObject> {
|
||||||
|
const tokens = root.tokenize(search);
|
||||||
|
if (tokens.length === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
const scores = ({});
|
||||||
|
const hitCounts = ({});
|
||||||
|
for (const token of tokens) {
|
||||||
|
const matches = root.lookup(token);
|
||||||
|
for (const id in matches) {
|
||||||
|
scores[id] = (scores[id] ?? 0) + matches[id];
|
||||||
|
hitCounts[id] = (hitCounts[id] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranked = Object.keys(scores).filter(id => hitCounts[id] === tokens.length).sort((a, b) => scores[b] - scores[a] || (parseInt(a) - parseInt(b))).slice(0, 25);
|
||||||
|
|
||||||
|
const all = entries.instances;
|
||||||
|
const out = ranked.map(id => all[parseInt(id)]).filter(e => e !== undefined);
|
||||||
|
|
||||||
|
if (out.length < 5 && root.fzfFinder) {
|
||||||
|
const seen = ({});
|
||||||
|
for (const id of ranked)
|
||||||
|
seen[id] = true;
|
||||||
|
const fuzzy = root.fzfFinder.find(search);
|
||||||
|
for (const r of fuzzy) {
|
||||||
|
const idx = r.item.idx;
|
||||||
|
if (seen[idx])
|
||||||
|
continue;
|
||||||
|
seen[idx] = true;
|
||||||
|
const entry = all[idx];
|
||||||
|
if (entry !== undefined)
|
||||||
|
out.push(entry);
|
||||||
|
if (out.length >= 25)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenize(text: string): var {
|
||||||
|
return text.toLowerCase().split(/[^a-z0-9]+/).filter(t => t.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(ZUtils.settingsIndex());
|
||||||
|
entries.model = data.entries;
|
||||||
|
root.inverted = data.inverted ?? {};
|
||||||
|
root.ranking = data.ranking ?? {};
|
||||||
|
const docs = data.entries.map((e, i) => ({
|
||||||
|
idx: i,
|
||||||
|
text: e.title
|
||||||
|
}));
|
||||||
|
root.fzfFinder = new Fzf.Finder(docs, {
|
||||||
|
selector: d => d.text,
|
||||||
|
limit: 25
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
entries.model = [];
|
||||||
|
root.inverted = {};
|
||||||
|
root.ranking = {};
|
||||||
|
root.fzfFinder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: entries
|
||||||
|
|
||||||
|
SettingEntry {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SettingEntry: QtObject {
|
||||||
|
readonly property string anchor: modelData.anchor ?? ""
|
||||||
|
readonly property var crumbIcons: modelData.crumbIcons
|
||||||
|
readonly property var crumbLabels: modelData.crumbLabels
|
||||||
|
readonly property bool isToggle: togglePath.length > 0
|
||||||
|
required property var modelData
|
||||||
|
readonly property int pageIdx: modelData.pageIdx
|
||||||
|
readonly property string section: modelData.section ?? ""
|
||||||
|
readonly property var subPath: modelData.subPath
|
||||||
|
readonly property string subtext: modelData.subtext ?? ""
|
||||||
|
readonly property string title: modelData.title
|
||||||
|
readonly property string togglePath: modelData.togglePath ?? ""
|
||||||
|
readonly property bool toggleValue: {
|
||||||
|
if (!isToggle)
|
||||||
|
return false;
|
||||||
|
let obj = Config;
|
||||||
|
const parts = togglePath.split(".");
|
||||||
|
for (const part of parts) {
|
||||||
|
if (obj === undefined || obj === null)
|
||||||
|
return false;
|
||||||
|
obj = obj[part];
|
||||||
|
}
|
||||||
|
return obj ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setToggle(value: bool): void {
|
||||||
|
if (!isToggle)
|
||||||
|
return;
|
||||||
|
const parts = togglePath.split(".");
|
||||||
|
let obj = Config;
|
||||||
|
for (let k = 0; k < parts.length - 1; k++) {
|
||||||
|
if (obj === undefined || obj === null)
|
||||||
|
return;
|
||||||
|
obj = obj[parts[k]];
|
||||||
|
}
|
||||||
|
if (obj !== undefined && obj !== null)
|
||||||
|
obj[parts[parts.length - 1]] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,19 @@ QtObject {
|
|||||||
property bool animatingContainer
|
property bool animatingContainer
|
||||||
property int currentPageIdx
|
property int currentPageIdx
|
||||||
property bool isWindow
|
property bool isWindow
|
||||||
|
property string lastAnchor
|
||||||
|
property list<int> pendingSubPath
|
||||||
property ShellScreen screen
|
property ShellScreen screen
|
||||||
|
property string searchAnchor
|
||||||
property bool searchOpen
|
property bool searchOpen
|
||||||
|
property string searchText
|
||||||
property DesktopEntry selectedApp
|
property DesktopEntry selectedApp
|
||||||
property BluetoothDevice selectedBtDevice
|
property BluetoothDevice selectedBtDevice
|
||||||
property string selectedWallpaperCategory
|
property string selectedWallpaperCategory
|
||||||
property list<int> subPageIdxStack
|
property list<int> subPageIdxStack
|
||||||
|
|
||||||
signal close
|
signal close
|
||||||
|
signal highlightSetting(anchor: string)
|
||||||
signal subPageClosed
|
signal subPageClosed
|
||||||
signal subPageOpened(idx: int)
|
signal subPageOpened(idx: int)
|
||||||
|
|
||||||
@@ -24,10 +29,38 @@ QtObject {
|
|||||||
subPageIdxStack.pop();
|
subPageIdxStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jumpToSetting(pageIdx: int, subPath: var, anchor: string): void {
|
||||||
|
const samePage = currentPageIdx === pageIdx;
|
||||||
|
const sameSub = subPageIdxStack.length === subPath.length && subPath.every((v, i) => subPageIdxStack[i] === v);
|
||||||
|
if (samePage && sameSub && anchor === lastAnchor) {
|
||||||
|
highlightSetting(anchor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastAnchor = anchor;
|
||||||
|
if (samePage && sameSub) {
|
||||||
|
searchAnchor = "";
|
||||||
|
searchAnchor = anchor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchAnchor = anchor;
|
||||||
|
if (!samePage) {
|
||||||
|
pendingSubPath = subPath.slice();
|
||||||
|
currentPageIdx = pageIdx;
|
||||||
|
} else {
|
||||||
|
while (subPageIdxStack.length > 0)
|
||||||
|
closeSubPage();
|
||||||
|
for (let i = 0; i < subPath.length; i++)
|
||||||
|
openSubPage(subPath[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openSubPage(idx: int): void {
|
function openSubPage(idx: int): void {
|
||||||
subPageIdxStack.push(idx);
|
subPageIdxStack.push(idx);
|
||||||
subPageOpened(idx);
|
subPageOpened(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentPageIdxChanged: subPageIdxStack.length = 0
|
onCurrentPageIdxChanged: {
|
||||||
|
subPageIdxStack = pendingSubPath;
|
||||||
|
pendingSubPath = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import qs.Components
|
import qs.Components
|
||||||
@@ -12,14 +13,13 @@ import qs.Config
|
|||||||
StackView {
|
StackView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int biggestWidth: 0
|
|
||||||
readonly property int itemHeight: 30
|
readonly property int itemHeight: 30
|
||||||
readonly property int panelRadius: ((itemHeight / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
readonly property int panelRadius: ((itemHeight / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
||||||
required property PopoutState popouts
|
required property PopoutState popouts
|
||||||
property int rootWidth: 0
|
property int rootWidth: 0
|
||||||
required property QsMenuHandle trayItem
|
required property QsMenuHandle trayItem
|
||||||
|
|
||||||
implicitHeight: currentItem.implicitHeight
|
implicitHeight: currentItem.isSubMenu ? currentItem.implicitHeight : currentItem.implicitHeight - currentItem.spacing
|
||||||
implicitWidth: currentItem.implicitWidth
|
implicitWidth: currentItem.implicitWidth
|
||||||
|
|
||||||
initialItem: SubMenu {
|
initialItem: SubMenu {
|
||||||
@@ -46,7 +46,7 @@ StackView {
|
|||||||
duration: 0
|
duration: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
component SubMenu: Column {
|
component SubMenu: ColumnLayout {
|
||||||
id: menu
|
id: menu
|
||||||
|
|
||||||
required property QsMenuHandle handle
|
required property QsMenuHandle handle
|
||||||
@@ -54,9 +54,8 @@ StackView {
|
|||||||
property bool shown
|
property bool shown
|
||||||
|
|
||||||
opacity: shown ? 1 : 0
|
opacity: shown ? 1 : 0
|
||||||
padding: 0
|
|
||||||
scale: shown ? 1 : 0.8
|
scale: shown ? 1 : 0.8
|
||||||
spacing: 4
|
spacing: Appearance.spacing.extraSmall
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -87,22 +86,27 @@ StackView {
|
|||||||
required property int index
|
required property int index
|
||||||
required property QsMenuEntry modelData
|
required property QsMenuEntry modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: modelData.isSeparator ? Appearance.padding.normal : 0
|
||||||
|
Layout.rightMargin: modelData.isSeparator ? Appearance.padding.normal : 0
|
||||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
||||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
implicitHeight: modelData.isSeparator ? (visible ? 1 : 0) : childrenLoader.item.implicitHeight
|
||||||
implicitWidth: root.biggestWidth
|
implicitWidth: childrenLoader.item?.implicitWidth ?? 0
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: children
|
id: childrenLoader
|
||||||
|
|
||||||
active: !item.modelData.isSeparator
|
active: !item.modelData.isSeparator
|
||||||
anchors.left: parent.left
|
anchors.fill: parent
|
||||||
anchors.right: parent.right
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
|
property int iconWidth: icon.active ? icon.width + Appearance.spacing.normal + icon.anchors.rightMargin : 0
|
||||||
|
|
||||||
implicitHeight: root.itemHeight
|
implicitHeight: root.itemHeight
|
||||||
|
implicitWidth: label.width + label.anchors.leftMargin * 2 + iconWidth + (expand.item?.width ?? 0)
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
enabled: item.modelData.enabled
|
enabled: item.modelData.enabled
|
||||||
@@ -111,8 +115,6 @@ StackView {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
const entry = item.modelData;
|
const entry = item.modelData;
|
||||||
if (entry.hasChildren) {
|
if (entry.hasChildren) {
|
||||||
root.rootWidth = root.biggestWidth;
|
|
||||||
root.biggestWidth = 0;
|
|
||||||
root.push(subMenuComp.createObject(null, {
|
root.push(subMenuComp.createObject(null, {
|
||||||
handle: entry,
|
handle: entry,
|
||||||
isSubMenu: true
|
isSubMenu: true
|
||||||
@@ -161,23 +163,7 @@ StackView {
|
|||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||||
text: labelMetrics.elidedText
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: labelMetrics
|
|
||||||
|
|
||||||
font.family: label.font.family
|
|
||||||
font.pointSize: label.font.pointSize
|
|
||||||
text: item.modelData.text
|
text: item.modelData.text
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var biggestWidth = root.biggestWidth;
|
|
||||||
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
|
|
||||||
if (currentWidth > biggestWidth) {
|
|
||||||
root.biggestWidth = currentWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -201,17 +187,17 @@ StackView {
|
|||||||
Loader {
|
Loader {
|
||||||
id: loader
|
id: loader
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: active ? implicitHeight : 0
|
||||||
active: menu.isSubMenu
|
active: menu.isSubMenu
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
implicitHeight: 30
|
implicitHeight: 30
|
||||||
implicitWidth: back.implicitWidth
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.bottom: parent.bottom
|
anchors.fill: parent
|
||||||
implicitHeight: 30
|
implicitHeight: 30
|
||||||
implicitWidth: root.biggestWidth
|
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -224,7 +210,6 @@ StackView {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.pop();
|
root.pop();
|
||||||
root.biggestWidth = root.rootWidth;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ Item {
|
|||||||
CustomRect {
|
CustomRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 3
|
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
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
@@ -57,7 +58,7 @@ Item {
|
|||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
root.item.activate();
|
root.item.activate();
|
||||||
console.log(icon.source + "\n" + root.item.id);
|
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.currentName = `traymenu${root.ind}`;
|
||||||
root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x);
|
root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x);
|
||||||
root.popouts.hasCurrent = true;
|
root.popouts.hasCurrent = true;
|
||||||
@@ -76,9 +77,9 @@ Item {
|
|||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
antialiasing: true
|
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
|
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
|
scale: 1 / root.dpr
|
||||||
source: root.resolveIcon(root.item.id, root.item.icon)
|
source: root.resolveIcon(root.item.id, root.item.icon)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let trayPos = mapToItem(sysTray, localX, localY);
|
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 trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y);
|
||||||
let child = sysRow.childAt(trayRowPos.x, trayRowPos.y);
|
let child = sysRow.childAt(trayRowPos.x, trayRowPos.y);
|
||||||
if (child && child.hasOwnProperty("popoutId")) {
|
if (child && child.hasOwnProperty("ind")) {
|
||||||
return {
|
return {
|
||||||
id: child.popoutId,
|
id: `traymenu${child.ind}`,
|
||||||
item: child
|
item: child
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ void BlobGroup::setColor(const QColor& c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::setCornerFill(bool e) {
|
void BlobGroup::setCornerFill(bool e) {
|
||||||
if (m_cornerFill == e)
|
if (m_cornerFill == e) return;
|
||||||
return;
|
|
||||||
m_cornerFill = e;
|
m_cornerFill = e;
|
||||||
emit cornerFillChanged();
|
emit cornerFillChanged();
|
||||||
markDirty();
|
markDirty();
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ class BlobShape;
|
|||||||
class BlobInvertedRect;
|
class BlobInvertedRect;
|
||||||
|
|
||||||
class BlobGroup : public QObject {
|
class BlobGroup : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
|
||||||
Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFillChanged)
|
smoothingChanged)
|
||||||
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY
|
||||||
|
cornerFillChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobGroup(QObject* parent = nullptr);
|
explicit BlobGroup(QObject* parent = nullptr);
|
||||||
@@ -27,14 +31,12 @@ Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFill
|
|||||||
|
|
||||||
void setColor(const QColor& c);
|
void setColor(const QColor& c);
|
||||||
|
|
||||||
[[nodiscard]] bool cornerFill() const {
|
[[nodiscard]] bool cornerFill() const { return m_cornerFill; }
|
||||||
return m_cornerFill;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCornerFill(bool e);
|
void setCornerFill(bool e);
|
||||||
|
|
||||||
void addShape(BlobShape* shape);
|
void addShape(BlobShape* shape);
|
||||||
void removeShape(BlobShape* shape);
|
void removeShape(BlobShape* shape);
|
||||||
|
|
||||||
void setInvertedRect(BlobInvertedRect* rect);
|
void setInvertedRect(BlobInvertedRect* rect);
|
||||||
void clearInvertedRect(BlobInvertedRect* rect);
|
void clearInvertedRect(BlobInvertedRect* rect);
|
||||||
@@ -49,16 +51,16 @@ void removeShape(BlobShape* shape);
|
|||||||
void markShapeDirty(BlobShape* source);
|
void markShapeDirty(BlobShape* source);
|
||||||
void ensurePhysicsUpdated();
|
void ensurePhysicsUpdated();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void smoothingChanged();
|
void smoothingChanged();
|
||||||
void colorChanged();
|
void colorChanged();
|
||||||
void cornerFillChanged();
|
void cornerFillChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qreal m_smoothing = 32.0;
|
qreal m_smoothing = 32.0;
|
||||||
QColor m_color{ 0x44, 0x88, 0xff };
|
QColor m_color{0x44, 0x88, 0xff};
|
||||||
bool m_cornerFill = true;
|
bool m_cornerFill = true;
|
||||||
QList<BlobShape*> m_shapes;
|
QList<BlobShape*> m_shapes;
|
||||||
BlobInvertedRect* m_invertedRect = nullptr;
|
BlobInvertedRect* m_invertedRect = nullptr;
|
||||||
bool m_physicsUpdated = false;
|
bool m_physicsUpdated = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,11 +32,9 @@ void BlobRect::updatePolish() {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
},
|
||||||
},
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
@@ -184,8 +182,7 @@ bool BlobRect::isExcluded(const BlobShape* other) const {
|
|||||||
|
|
||||||
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
|
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
|
||||||
for (const auto& ptr : m_excludeCorners) {
|
for (const auto& ptr : m_excludeCorners) {
|
||||||
if (ptr == other)
|
if (ptr == other) return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -203,8 +200,15 @@ QQmlListProperty<BlobRect> BlobRect::exclude() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
|
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
|
||||||
return QQmlListProperty<BlobRect>(this, nullptr, &excludeCornersAppend, &excludeCornersCount, &excludeCornersAt,
|
return QQmlListProperty<BlobRect>(
|
||||||
&excludeCornersClear, &excludeCornersReplace, &excludeCornersRemoveLast);
|
this,
|
||||||
|
nullptr,
|
||||||
|
&excludeCornersAppend,
|
||||||
|
&excludeCornersCount,
|
||||||
|
&excludeCornersAt,
|
||||||
|
&excludeCornersClear,
|
||||||
|
&excludeCornersReplace,
|
||||||
|
&excludeCornersRemoveLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
@@ -249,11 +253,11 @@ void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
|||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeCornersAppend(
|
||||||
|
QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_excludeCorners.append(rect);
|
self->m_excludeCorners.append(rect);
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,36 +266,33 @@ qsizetype BlobRect::excludeCornersCount(QQmlListProperty<BlobRect>* prop) {
|
|||||||
return self->m_excludeCorners.size();
|
return self->m_excludeCorners.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobRect* BlobRect::excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
BlobRect* BlobRect::excludeCornersAt(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
return self->m_excludeCorners.at(index);
|
return self->m_excludeCorners.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_excludeCorners.isEmpty())
|
if (self->m_excludeCorners.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_excludeCorners.clear();
|
self->m_excludeCorners.clear();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
void BlobRect::excludeCornersReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_excludeCorners[index] = rect;
|
self->m_excludeCorners[index] = rect;
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_excludeCorners.isEmpty())
|
if (self->m_excludeCorners.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_excludeCorners.removeLast();
|
self->m_excludeCorners.removeLast();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,11 +320,9 @@ void BlobRect::checkAtRest(float speed) {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
},
|
||||||
},
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,32 @@
|
|||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
|
||||||
class BlobRect : public BlobShape {
|
class BlobRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
qreal stiffness READ stiffness WRITE setStiffness NOTIFY
|
||||||
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
stiffnessChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> excludeCorners READ excludeCorners NOTIFY excludeCornersChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
|
qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
|
||||||
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
|
deformScaleChanged)
|
||||||
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(
|
QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
||||||
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<BlobRect> excludeCorners READ excludeCorners NOTIFY
|
||||||
|
excludeCornersChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY
|
||||||
|
topLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY
|
||||||
|
topRightRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius
|
||||||
|
NOTIFY bottomLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomRightRadius READ bottomRightRadius WRITE
|
||||||
|
setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobRect(QQuickItem* parent = nullptr);
|
explicit BlobRect(QQuickItem* parent = nullptr);
|
||||||
@@ -52,12 +66,12 @@ Q_PROPERTY(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> exclude();
|
QQmlListProperty<BlobRect> exclude();
|
||||||
QQmlListProperty<BlobRect> excludeCorners();
|
QQmlListProperty<BlobRect> excludeCorners();
|
||||||
|
|
||||||
bool isExcluded(const BlobShape* other) const override;
|
bool isExcluded(const BlobShape* other) const override;
|
||||||
bool isCornerExcluded(const BlobShape* other) const override;
|
bool isCornerExcluded(const BlobShape* other) const override;
|
||||||
void cornerRadii(float out[4]) const override;
|
void cornerRadii(float out[4]) const override;
|
||||||
|
|
||||||
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
|
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
|
||||||
|
|
||||||
@@ -77,16 +91,16 @@ void cornerRadii(float out[4]) const override;
|
|||||||
|
|
||||||
void setBottomRightRadius(qreal r);
|
void setBottomRightRadius(qreal r);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stiffnessChanged();
|
void stiffnessChanged();
|
||||||
void dampingChanged();
|
void dampingChanged();
|
||||||
void deformScaleChanged();
|
void deformScaleChanged();
|
||||||
void excludeChanged();
|
void excludeChanged();
|
||||||
void excludeCornersChanged();
|
void excludeCornersChanged();
|
||||||
void topLeftRadiusChanged();
|
void topLeftRadiusChanged();
|
||||||
void topRightRadiusChanged();
|
void topRightRadiusChanged();
|
||||||
void bottomLeftRadiusChanged();
|
void bottomLeftRadiusChanged();
|
||||||
void bottomRightRadiusChanged();
|
void bottomRightRadiusChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
@@ -121,20 +135,25 @@ void bottomRightRadiusChanged();
|
|||||||
qreal m_bottomLeftRadius = -1;
|
qreal m_bottomLeftRadius = -1;
|
||||||
qreal m_bottomRightRadius = -1;
|
qreal m_bottomRightRadius = -1;
|
||||||
|
|
||||||
QList<QPointer<BlobRect> > m_exclude;
|
QList<QPointer<BlobRect>> m_exclude;
|
||||||
QList<QPointer<BlobRect> > m_excludeCorners;
|
QList<QPointer<BlobRect>> m_excludeCorners;
|
||||||
|
|
||||||
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
||||||
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
static BlobRect* excludeAt(
|
||||||
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
|
|
||||||
static void excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeCornersAppend(
|
||||||
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
|
QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static BlobRect* excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
|
static BlobRect* excludeCornersAt(
|
||||||
static void excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static void excludeCornersReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ static float cornerFillFactor(float sd, float smoothFactor) {
|
|||||||
return std::max(outside, inside);
|
return std::max(outside, inside);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobShape::BlobShape(QQuickItem* parent)
|
BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
|
||||||
: QQuickItem(parent) {
|
|
||||||
setFlag(ItemHasContents);
|
setFlag(ItemHasContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,13 +311,10 @@ void BlobShape::updatePolish() {
|
|||||||
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
||||||
|
|
||||||
for (qsizetype j = 0; cornerFill && j < rectCount; ++j) {
|
for (qsizetype j = 0; cornerFill && j < rectCount; ++j) {
|
||||||
if (j == i)
|
if (j == i) continue;
|
||||||
continue;
|
if (riExcludeMask & (1 << j)) continue;
|
||||||
if (riExcludeMask & (1 << j))
|
|
||||||
continue;
|
|
||||||
BlobShape* const sj = rectShapes[j];
|
BlobShape* const sj = rectShapes[j];
|
||||||
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si))
|
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si)) continue;
|
||||||
continue;
|
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
const float sdTr = cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
const float sdTr = cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
||||||
const float sdBr = cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
const float sdBr = cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ class BlobShape : public QQuickItem {
|
|||||||
|
|
||||||
virtual bool isExcluded(const BlobShape*) const { return false; }
|
virtual bool isExcluded(const BlobShape*) const { return false; }
|
||||||
|
|
||||||
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
|
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void cornerRadii(float out[4]) const;
|
virtual void cornerRadii(float out[4]) const;
|
||||||
|
|
||||||
virtual void updatePhysics() {}
|
virtual void updatePhysics() {}
|
||||||
|
|
||||||
@@ -67,25 +67,25 @@ virtual void cornerRadii(float out[4]) const;
|
|||||||
virtual void unregisterFromGroup();
|
virtual void unregisterFromGroup();
|
||||||
void updateCenteredDeformMatrix();
|
void updateCenteredDeformMatrix();
|
||||||
|
|
||||||
BlobGroup* m_group = nullptr;
|
BlobGroup* m_group = nullptr;
|
||||||
qreal m_radius = 0;
|
qreal m_radius = 0;
|
||||||
QMatrix4x4 m_deformMatrix; // identity by default
|
QMatrix4x4 m_deformMatrix; // identity by default
|
||||||
QMatrix4x4 m_centeredDeformMatrix;
|
QMatrix4x4 m_centeredDeformMatrix;
|
||||||
|
|
||||||
// Cached data from updatePolish
|
// Cached data from updatePolish
|
||||||
float m_cachedPaddedX = 0;
|
float m_cachedPaddedX = 0;
|
||||||
float m_cachedPaddedY = 0;
|
float m_cachedPaddedY = 0;
|
||||||
float m_cachedPaddedW = 0;
|
float m_cachedPaddedW = 0;
|
||||||
float m_cachedPaddedH = 0;
|
float m_cachedPaddedH = 0;
|
||||||
float m_pendingDw = 0;
|
float m_pendingDw = 0;
|
||||||
float m_pendingDh = 0;
|
float m_pendingDh = 0;
|
||||||
QRectF m_localPaddedRect;
|
QRectF m_localPaddedRect;
|
||||||
QVector<BlobRectData> m_cachedRects;
|
QVector<BlobRectData> m_cachedRects;
|
||||||
int m_cachedMyIndex = -2;
|
int m_cachedMyIndex = -2;
|
||||||
float m_pendingDx = 0;
|
float m_pendingDx = 0;
|
||||||
float m_pendingDy = 0;
|
float m_pendingDy = 0;
|
||||||
bool m_cachedHasInverted = false;
|
bool m_cachedHasInverted = false;
|
||||||
float m_cachedInvertedRadius = 0;
|
float m_cachedInvertedRadius = 0;
|
||||||
float m_cachedInvertedOuter[4] = {};
|
float m_cachedInvertedOuter[4] = {};
|
||||||
float m_cachedInvertedInner[4] = {};
|
float m_cachedInvertedInner[4] = {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
|
|||||||
qt_standard_project_setup(REQUIRES 6.9)
|
qt_standard_project_setup(REQUIRES 6.9)
|
||||||
|
|
||||||
function(qml_module arg_TARGET)
|
function(qml_module arg_TARGET)
|
||||||
cmake_parse_arguments(PARSE_ARGV 1 arg "" "URI" "SOURCES;LIBRARIES")
|
cmake_parse_arguments(PARSE_ARGV 1 arg "" "URI" "SOURCES;LIBRARIES;RESOURCES")
|
||||||
qt_add_qml_module(${arg_TARGET}
|
qt_add_qml_module(${arg_TARGET}
|
||||||
URI ${arg_URI}
|
URI ${arg_URI}
|
||||||
VERSION 1.0
|
VERSION 1.0
|
||||||
SOURCES ${arg_SOURCES}
|
SOURCES ${arg_SOURCES}
|
||||||
|
RESOURCES ${arg_RESOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_query_qml_module(${arg_TARGET}
|
qt_query_qml_module(${arg_TARGET}
|
||||||
@@ -47,6 +48,8 @@ function(qml_module arg_TARGET)
|
|||||||
target_link_libraries(${arg_TARGET} PRIVATE Qt::Core Qt::Qml ${arg_LIBRARIES})
|
target_link_libraries(${arg_TARGET} PRIVATE Qt::Core Qt::Qml ${arg_LIBRARIES})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
set_source_files_properties("${SETTINGS_INDEX_JSON}" PROPERTIES QT_RESOURCE_ALIAS "settings-index.json")
|
||||||
|
|
||||||
qml_module(ZShell
|
qml_module(ZShell
|
||||||
URI ZShell
|
URI ZShell
|
||||||
SOURCES
|
SOURCES
|
||||||
@@ -57,6 +60,8 @@ qml_module(ZShell
|
|||||||
toaster.hpp toaster.cpp
|
toaster.hpp toaster.cpp
|
||||||
qalculator.hpp qalculator.cpp
|
qalculator.hpp qalculator.cpp
|
||||||
zutils.hpp zutils.cpp
|
zutils.hpp zutils.cpp
|
||||||
|
RESOURCES
|
||||||
|
"${SETTINGS_INDEX_JSON}"
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
#include "sensorslib.hpp"
|
#include "sensorslib.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
|
#include <qobject.h>
|
||||||
#include <qregularexpression.h>
|
#include <qregularexpression.h>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@@ -243,6 +245,45 @@ void Gpu::detectNameOnce() {
|
|||||||
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gpu::readGenericMemory() {
|
||||||
|
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
||||||
|
.entryList(
|
||||||
|
QStringList() << QStringLiteral("card*"),
|
||||||
|
QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
qreal totalMem = 0.0;
|
||||||
|
qreal usedMem = 0.0;
|
||||||
|
for (const QString& card : paths) {
|
||||||
|
QFile total(
|
||||||
|
QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_total")
|
||||||
|
.arg(card));
|
||||||
|
if (!total.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool ok = false;
|
||||||
|
const qreal v = total.readAll().trimmed().toDouble(&ok);
|
||||||
|
total.close();
|
||||||
|
if (ok) {
|
||||||
|
totalMem += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile used(QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_used")
|
||||||
|
.arg(card));
|
||||||
|
if (!used.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool ok1 = false;
|
||||||
|
const qreal v1 = used.readAll().trimmed().toDouble(&ok1);
|
||||||
|
used.close();
|
||||||
|
if (ok1) {
|
||||||
|
usedMem += v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMemoryTotal(totalMem);
|
||||||
|
setMemoryUsed(usedMem);
|
||||||
|
}
|
||||||
|
|
||||||
void Gpu::readGenericUsage() {
|
void Gpu::readGenericUsage() {
|
||||||
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
||||||
.entryList(
|
.entryList(
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class Gpu : public TickingService {
|
|||||||
void readGenericUsage();
|
void readGenericUsage();
|
||||||
void startNvidiaUsage();
|
void startNvidiaUsage();
|
||||||
void readGpuTemperature();
|
void readGpuTemperature();
|
||||||
|
void readGenericMemory();
|
||||||
|
|
||||||
void setUserType(Type value);
|
void setUserType(Type value);
|
||||||
void setAutoType(Type value);
|
void setAutoType(Type value);
|
||||||
|
|||||||
+39
-26
@@ -10,23 +10,28 @@ Q_LOGGING_CATEGORY(lcAppDb, "ZShell.appdb", QtInfoMsg)
|
|||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_entry(entry), m_frequency(frequency) {
|
||||||
, m_entry(entry)
|
|
||||||
, m_frequency(frequency) {
|
|
||||||
const auto mo = m_entry->metaObject();
|
const auto mo = m_entry->metaObject();
|
||||||
const auto tmo = &AppEntry::staticMetaObject;
|
const auto tmo = &AppEntry::staticMetaObject;
|
||||||
|
|
||||||
for (const auto& prop :
|
for (const auto& prop :
|
||||||
{ "name", "comment", "execString", "startupClass", "genericName", "categories", "keywords" }) {
|
{"name",
|
||||||
|
"comment",
|
||||||
|
"execString",
|
||||||
|
"startupClass",
|
||||||
|
"genericName",
|
||||||
|
"categories",
|
||||||
|
"keywords"}) {
|
||||||
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
||||||
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
||||||
QObject::connect(m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
QObject::connect(
|
||||||
|
m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
||||||
m_entry = nullptr;
|
m_entry = nullptr;
|
||||||
deleteLater();
|
deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* AppEntry::entry() const {
|
QObject* AppEntry::entry() const {
|
||||||
@@ -118,7 +123,9 @@ AppDb::AppDb(QObject* parent)
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AppDb::uuid() const {
|
QString AppDb::uuid() const {
|
||||||
@@ -145,7 +152,9 @@ void AppDb::setPath(const QString& path) {
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
|
|
||||||
updateAppFrequencies();
|
updateAppFrequencies();
|
||||||
}
|
}
|
||||||
@@ -183,7 +192,9 @@ void AppDb::setFavoriteApps(const QStringList& favApps) {
|
|||||||
if (re.isValid()) {
|
if (re.isValid()) {
|
||||||
m_favoriteAppsRegex << re;
|
m_favoriteAppsRegex << re;
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcAppDb) << "setFavoriteApps: regular expression is not valid:" << re.pattern();
|
qCWarning(lcAppDb)
|
||||||
|
<< "setFavoriteApps: regular expression is not valid:"
|
||||||
|
<< re.pattern();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +202,7 @@ void AppDb::setFavoriteApps(const QStringList& favApps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString AppDb::regexifyString(const QString& original) const {
|
QString AppDb::regexifyString(const QString& original) const {
|
||||||
if (original.startsWith('^') && original.endsWith('$'))
|
if (original.startsWith('^') && original.endsWith('$')) return original;
|
||||||
return original;
|
|
||||||
|
|
||||||
const QString escaped = QRegularExpression::escape(original);
|
const QString escaped = QRegularExpression::escape(original);
|
||||||
return QStringLiteral("^%1$").arg(escaped);
|
return QStringLiteral("^%1$").arg(escaped);
|
||||||
@@ -206,9 +216,10 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
auto db = QSqlDatabase::database(m_uuid);
|
auto db = QSqlDatabase::database(m_uuid);
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
|
|
||||||
query.prepare("INSERT INTO frequencies (id, frequency) "
|
query.prepare(
|
||||||
"VALUES (:id, 1) "
|
"INSERT INTO frequencies (id, frequency) "
|
||||||
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
"VALUES (:id, 1) "
|
||||||
|
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
||||||
query.bindValue(":id", id);
|
query.bindValue(":id", id);
|
||||||
query.exec();
|
query.exec();
|
||||||
|
|
||||||
@@ -221,7 +232,8 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
emit appsChanged();
|
emit appsChanged();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcAppDb) << "incrementFrequency: could not find app with id" << id;
|
qCWarning(lcAppDb) << "incrementFrequency: could not find app with id"
|
||||||
|
<< id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,15 +244,16 @@ QList<AppEntry*>& AppDb::getSortedApps() const {
|
|||||||
QSet<QString> favSet;
|
QSet<QString> favSet;
|
||||||
favSet.reserve(m_sortedApps.size());
|
favSet.reserve(m_sortedApps.size());
|
||||||
for (const auto* app : std::as_const(m_sortedApps)) {
|
for (const auto* app : std::as_const(m_sortedApps)) {
|
||||||
if (isFavorite(app))
|
if (isFavorite(app)) favSet.insert(app->id());
|
||||||
favSet.insert(app->id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(m_sortedApps.begin(), m_sortedApps.end(), [&favSet](AppEntry* a, AppEntry* b) {
|
std::sort(
|
||||||
|
m_sortedApps.begin(),
|
||||||
|
m_sortedApps.end(),
|
||||||
|
[&favSet](AppEntry* a, AppEntry* b) {
|
||||||
const bool aIsFav = favSet.contains(a->id());
|
const bool aIsFav = favSet.contains(a->id());
|
||||||
const bool bIsFav = favSet.contains(b->id());
|
const bool bIsFav = favSet.contains(b->id());
|
||||||
if (aIsFav != bIsFav)
|
if (aIsFav != bIsFav) return aIsFav;
|
||||||
return aIsFav;
|
|
||||||
if (a->frequency() != b->frequency())
|
if (a->frequency() != b->frequency())
|
||||||
return a->frequency() > b->frequency();
|
return a->frequency() > b->frequency();
|
||||||
return a->name().localeAwareCompare(b->name()) < 0;
|
return a->name().localeAwareCompare(b->name()) < 0;
|
||||||
@@ -293,10 +306,10 @@ void AppDb::updateApps() {
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
auto* const newEntry = new AppEntry(entry, getFrequency(id), this);
|
auto* const newEntry = new AppEntry(entry, getFrequency(id), this);
|
||||||
QObject::connect(newEntry, &QObject::destroyed, this, [id, this]() {
|
QObject::connect(newEntry, &QObject::destroyed, this, [id, this]() {
|
||||||
if (m_apps.remove(id)) {
|
if (m_apps.remove(id)) {
|
||||||
emit appsChanged();
|
emit appsChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
m_apps.insert(id, newEntry);
|
m_apps.insert(id, newEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-26
@@ -65,11 +65,16 @@ class AppDb : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
||||||
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY favoriteAppsChanged REQUIRED)
|
QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY
|
||||||
|
favoriteAppsChanged REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppDb(QObject* parent = nullptr);
|
explicit AppDb(QObject* parent = nullptr);
|
||||||
@@ -82,36 +87,36 @@ Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
|||||||
[[nodiscard]] QObjectList entries() const;
|
[[nodiscard]] QObjectList entries() const;
|
||||||
void setEntries(const QObjectList& entries);
|
void setEntries(const QObjectList& entries);
|
||||||
|
|
||||||
[[nodiscard]] QStringList favoriteApps() const;
|
[[nodiscard]] QStringList favoriteApps() const;
|
||||||
void setFavoriteApps(const QStringList& favApps);
|
void setFavoriteApps(const QStringList& favApps);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<AppEntry> apps();
|
[[nodiscard]] QQmlListProperty<AppEntry> apps();
|
||||||
|
|
||||||
Q_INVOKABLE void incrementFrequency(const QString& id);
|
Q_INVOKABLE void incrementFrequency(const QString& id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
void entriesChanged();
|
void entriesChanged();
|
||||||
void favoriteAppsChanged();
|
void favoriteAppsChanged();
|
||||||
void appsChanged();
|
void appsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer* m_timer;
|
QTimer* m_timer;
|
||||||
|
|
||||||
const QString m_uuid;
|
const QString m_uuid;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QObjectList m_entries;
|
QObjectList m_entries;
|
||||||
QStringList m_favoriteApps;
|
QStringList m_favoriteApps;
|
||||||
QList<QRegularExpression> m_favoriteAppsRegex;
|
QList<QRegularExpression> m_favoriteAppsRegex;
|
||||||
QHash<QString, AppEntry*> m_apps;
|
QHash<QString, AppEntry*> m_apps;
|
||||||
mutable QList<AppEntry*> m_sortedApps;
|
mutable QList<AppEntry*> m_sortedApps;
|
||||||
|
|
||||||
QString regexifyString(const QString& original) const;
|
QString regexifyString(const QString& original) const;
|
||||||
QList<AppEntry*>& getSortedApps() const;
|
QList<AppEntry*>& getSortedApps() const;
|
||||||
bool isFavorite(const AppEntry* app) const;
|
bool isFavorite(const AppEntry* app) const;
|
||||||
quint32 getFrequency(const QString& id) const;
|
quint32 getFrequency(const QString& id) const;
|
||||||
void updateAppFrequencies();
|
void updateAppFrequencies();
|
||||||
void updateApps();
|
void updateApps();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell
|
} // namespace ZShell
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <qjsprimitivevalue.h>
|
#include <qjsprimitivevalue.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
|
#include <qfile.h>
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcZUtils, "ZShell.cutils", QtInfoMsg)
|
Q_LOGGING_CATEGORY(lcZUtils, "ZShell.cutils", QtInfoMsg)
|
||||||
|
|
||||||
@@ -170,6 +171,15 @@ qreal ZUtils::clamp(qreal value, qreal min, qreal max) {
|
|||||||
return qBound(min, value, max);
|
return qBound(min, value, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ZUtils::settingsIndex() {
|
||||||
|
QFile file(QStringLiteral(":/qt/qml/ZShell/settings-index.json"));
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qCWarning(lcZUtils) << "Failed to open embedded settings index";
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
return QString::fromUtf8(file.readAll());
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef ZSHELL_VERSION
|
#ifndef ZSHELL_VERSION
|
||||||
#define ZSHELL_VERSION ""
|
#define ZSHELL_VERSION ""
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+13
-11
@@ -12,18 +12,18 @@ class ZUtils : public QObject {
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
Q_PROPERTY(QString version READ version CONSTANT)
|
Q_PROPERTY(QString version READ version CONSTANT)
|
||||||
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
|
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// clang-format off
|
// clang-format off
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
|
||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
Q_INVOKABLE static bool copyFile(
|
Q_INVOKABLE static bool copyFile(
|
||||||
const QUrl& source, const QUrl& target, bool overwrite = true);
|
const QUrl& source, const QUrl& target, bool overwrite = true);
|
||||||
@@ -32,6 +32,8 @@ Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rec
|
|||||||
|
|
||||||
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
|
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
|
||||||
|
|
||||||
|
Q_INVOKABLE static QString settingsIndex();
|
||||||
|
|
||||||
[[nodiscard]] QString version() const;
|
[[nodiscard]] QString version() const;
|
||||||
[[nodiscard]] QString qtVersion() const;
|
[[nodiscard]] QString qtVersion() const;
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,407 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def read_lines(path: Path) -> tuple[str, ...]:
|
||||||
|
return tuple(path.read_text().splitlines())
|
||||||
|
|
||||||
|
|
||||||
|
ROW_RE = re.compile(
|
||||||
|
r'^\s*(ToggleRow|SliderRow|SelectRow|SpinRow|NavRow|InfoRow|PopupRow|OverlayRow|DefaultRow)\s*\{')
|
||||||
|
LABEL_RE = re.compile(r'^\s*(?:label|text):\s*qsTr\("([^"]+)"\)')
|
||||||
|
ANCHOR_RE = re.compile(r'^\s*settingAnchor:\s*"([^"]+)"')
|
||||||
|
CHECKED_RE = re.compile(r'^\s*checked:\s*(?:Config)\.([\w.]+)\s*$')
|
||||||
|
ONTOGGLED_RE = re.compile(
|
||||||
|
r'^\s*onToggled:\s*(?:Config)\.([\w.]+)\s*=\s*checked\s*$')
|
||||||
|
ICON_RE = re.compile(r'^\s*icon:\s*"([^"]+)"')
|
||||||
|
SKIP_LABELS = {"Muted", "None"}
|
||||||
|
FIELD_WEIGHT = {"title": 1.0, "keywords": 0.4}
|
||||||
|
STOPWORDS = {"the", "a", "an", "of", "and", "or", "to", "on", "in", "for"}
|
||||||
|
|
||||||
|
|
||||||
|
def find_pages_dir(settings: Path) -> Path:
|
||||||
|
return settings / "Pages"
|
||||||
|
|
||||||
|
|
||||||
|
def discover_files(settings: Path) -> dict[str, Path]:
|
||||||
|
files: dict[str, Path] = {}
|
||||||
|
for p in find_pages_dir(settings).rglob("*.qml"):
|
||||||
|
files[p.stem] = p
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
PAGE_NAME_RE = re.compile(r'^\s*name:\s*qsTr\("([^"]+)"\)')
|
||||||
|
PAGE_ICON_RE = re.compile(r'^\s*icon:\s*"([^"]+)"')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_page_registry(settings: Path) -> list[tuple[str, str]]:
|
||||||
|
text = (settings / "PageRegistry.qml").read_text().splitlines()
|
||||||
|
|
||||||
|
start = next(
|
||||||
|
i for i, line in enumerate(text)
|
||||||
|
if re.search(r'\bpages\s*:\s*\[', line)
|
||||||
|
)
|
||||||
|
|
||||||
|
out: list[tuple[str, str]] = []
|
||||||
|
i = start + 1
|
||||||
|
|
||||||
|
while i < len(text):
|
||||||
|
line = text[i].strip()
|
||||||
|
|
||||||
|
if line.startswith("]"):
|
||||||
|
break
|
||||||
|
|
||||||
|
if line.startswith("//") or not line:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if line.startswith("{"):
|
||||||
|
name = None
|
||||||
|
icon = None
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
while i < len(text):
|
||||||
|
s = text[i].strip()
|
||||||
|
|
||||||
|
if s.startswith("}"):
|
||||||
|
if name is not None:
|
||||||
|
out.append((icon or "tune", name))
|
||||||
|
break
|
||||||
|
|
||||||
|
if name is None:
|
||||||
|
m = PAGE_NAME_RE.match(text[i])
|
||||||
|
if m:
|
||||||
|
name = m.group(1)
|
||||||
|
|
||||||
|
if icon is None:
|
||||||
|
mi = PAGE_ICON_RE.match(text[i])
|
||||||
|
if mi:
|
||||||
|
icon = mi.group(1)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
BLOCK_RE = re.compile(r'^\s*([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*$')
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_comment(line: str) -> str:
|
||||||
|
return line.split("//", 1)[0].rstrip()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_block(lines: list[str], i: int) -> tuple[str, list[tuple[str, list]], int]:
|
||||||
|
line = _strip_comment(lines[i]).strip()
|
||||||
|
m = BLOCK_RE.match(line)
|
||||||
|
if not m:
|
||||||
|
raise ValueError(f"Expected block start at line {i + 1}: {lines[i]!r}")
|
||||||
|
|
||||||
|
name = m.group(1)
|
||||||
|
i += 1
|
||||||
|
children: list[tuple[str, list]] = []
|
||||||
|
|
||||||
|
while i < len(lines):
|
||||||
|
s = _strip_comment(lines[i]).strip()
|
||||||
|
if not s:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if s.startswith("}"):
|
||||||
|
return name, children, i + 1
|
||||||
|
|
||||||
|
if BLOCK_RE.match(s):
|
||||||
|
child_name, child_children, i = parse_block(lines, i)
|
||||||
|
children.append((child_name, child_children))
|
||||||
|
continue
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
raise ValueError(f"Unterminated block: {name}")
|
||||||
|
|
||||||
|
|
||||||
|
def collect_page_names(block: tuple[str, list[tuple[str, list]]]) -> list[str]:
|
||||||
|
name, children = block
|
||||||
|
|
||||||
|
if name != "Component":
|
||||||
|
return [name]
|
||||||
|
|
||||||
|
for child_name, child_children in children:
|
||||||
|
if child_name == "StackPage":
|
||||||
|
out: list[str] = []
|
||||||
|
for grand_name, grand_children in child_children:
|
||||||
|
if grand_name == "Component":
|
||||||
|
out.extend(collect_page_names((grand_name, grand_children)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
if child_name != "Component":
|
||||||
|
return [child_name]
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def parse_page_comps(settings: Path) -> list[list[str]]:
|
||||||
|
text = (settings / "PageCompRegistry.qml").read_text().splitlines()
|
||||||
|
|
||||||
|
start = next(
|
||||||
|
i for i, line in enumerate(text)
|
||||||
|
if re.search(r'\bpageComps\s*:\s*\[', _strip_comment(line))
|
||||||
|
)
|
||||||
|
|
||||||
|
comps: list[list[str]] = []
|
||||||
|
i = start + 1
|
||||||
|
|
||||||
|
while i < len(text):
|
||||||
|
s = _strip_comment(text[i]).strip()
|
||||||
|
if not s:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
if s.startswith("]"):
|
||||||
|
break
|
||||||
|
|
||||||
|
if BLOCK_RE.match(s) and BLOCK_RE.match(s).group(1) == "Component":
|
||||||
|
block = parse_block(text, i)
|
||||||
|
names = collect_page_names((block[0], block[1]))
|
||||||
|
if names:
|
||||||
|
comps.append(names)
|
||||||
|
i = block[2]
|
||||||
|
continue
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return comps
|
||||||
|
|
||||||
|
|
||||||
|
def dedup_crumbs(labels: list[str], icons: list[str]) -> tuple[list[str], list[str]]:
|
||||||
|
out_labels: list[str] = []
|
||||||
|
out_icons: list[str] = []
|
||||||
|
for lbl, ico in zip(labels, icons):
|
||||||
|
if out_labels and out_labels[-1] == lbl:
|
||||||
|
continue
|
||||||
|
out_labels.append(lbl)
|
||||||
|
out_icons.append(ico)
|
||||||
|
return out_labels, out_icons
|
||||||
|
|
||||||
|
|
||||||
|
def build_nav_map(settings: Path, files: dict[str, Path]) -> dict[str, dict]:
|
||||||
|
comps = parse_page_comps(settings)
|
||||||
|
registry = parse_page_registry(settings)
|
||||||
|
|
||||||
|
top_meta: dict[int, tuple[str, str]] = {}
|
||||||
|
for i, (icon, label) in enumerate(registry):
|
||||||
|
top_meta[i] = (icon, label)
|
||||||
|
|
||||||
|
nav_children: dict[str, dict[int, tuple[str, str, str]]] = {}
|
||||||
|
for names in comps:
|
||||||
|
for name in names:
|
||||||
|
pf = files.get(name)
|
||||||
|
if not pf:
|
||||||
|
continue
|
||||||
|
pending_icon = pending_label = None
|
||||||
|
section = ""
|
||||||
|
expect_section = False
|
||||||
|
for ln in read_lines(pf):
|
||||||
|
if SECTION_RE.match(ln):
|
||||||
|
expect_section = True
|
||||||
|
continue
|
||||||
|
ml = LABEL_RE.match(ln)
|
||||||
|
if ml:
|
||||||
|
if expect_section:
|
||||||
|
section = ml.group(1)
|
||||||
|
expect_section = False
|
||||||
|
else:
|
||||||
|
pending_label = ml.group(1)
|
||||||
|
continue
|
||||||
|
mi = ICON_RE.match(ln)
|
||||||
|
if mi:
|
||||||
|
pending_icon = mi.group(1)
|
||||||
|
mo = re.search(r"openSubPage\((\d+)\)", ln)
|
||||||
|
if mo:
|
||||||
|
pos = int(mo.group(1))
|
||||||
|
nav_children.setdefault(name, {})[pos] = (
|
||||||
|
pending_icon or "tune", pending_label or "", section)
|
||||||
|
pending_icon = pending_label = None
|
||||||
|
|
||||||
|
nav: dict[str, dict] = {}
|
||||||
|
for top_idx, names in enumerate(comps):
|
||||||
|
if not names:
|
||||||
|
continue
|
||||||
|
main = names[0]
|
||||||
|
main_icon, main_label = top_meta.get(top_idx, ("tune", main))
|
||||||
|
nav[main] = {"pageIdx": top_idx, "subPath": [],
|
||||||
|
"crumbIcons": [main_icon], "crumbLabels": [main_label]}
|
||||||
|
children = dict(nav_children.get(main, {}))
|
||||||
|
opened_via_subpage = set()
|
||||||
|
for owner, kids in nav_children.items():
|
||||||
|
owner_group = next((ns for ns in comps if owner in ns), None)
|
||||||
|
if not owner_group:
|
||||||
|
continue
|
||||||
|
for kpos in kids:
|
||||||
|
if kpos < len(owner_group):
|
||||||
|
opened_via_subpage.add(owner_group[kpos])
|
||||||
|
for pos in range(1, len(names)):
|
||||||
|
if pos not in children and names[pos] not in opened_via_subpage:
|
||||||
|
label = re.sub(r"(Detail)?Page$", "", names[pos])
|
||||||
|
label = re.sub(r"(?<!^)(?=[A-Z])", " ", label)
|
||||||
|
children[pos] = (main_icon, label, "")
|
||||||
|
for pos, (icon, label, section) in children.items():
|
||||||
|
if pos >= len(names):
|
||||||
|
continue
|
||||||
|
child = names[pos]
|
||||||
|
labels = [main_label] + ([section] if section else []) + [label]
|
||||||
|
icons = [main_icon] + ([icon] if section else []) + [icon]
|
||||||
|
labels, icons = dedup_crumbs(labels, icons)
|
||||||
|
nav[child] = {"pageIdx": top_idx, "subPath": [pos],
|
||||||
|
"crumbIcons": icons,
|
||||||
|
"crumbLabels": labels}
|
||||||
|
for gpos, (gicon, glabel, gsection) in nav_children.get(child, {}).items():
|
||||||
|
if gpos >= len(names):
|
||||||
|
continue
|
||||||
|
glabels = labels + ([gsection] if gsection else []) + [glabel]
|
||||||
|
gicons = icons + ([gicon] if gsection else []) + [gicon]
|
||||||
|
glabels, gicons = dedup_crumbs(glabels, gicons)
|
||||||
|
nav[names[gpos]] = {
|
||||||
|
"pageIdx": top_idx, "subPath": [pos, gpos],
|
||||||
|
"crumbIcons": gicons,
|
||||||
|
"crumbLabels": glabels}
|
||||||
|
return nav
|
||||||
|
|
||||||
|
|
||||||
|
def tokenize(text: str) -> list[str]:
|
||||||
|
toks: list[str] = []
|
||||||
|
for word in text.lower().split():
|
||||||
|
parts = [p for p in re.split(r"[^a-z0-9]+", word) if p]
|
||||||
|
for p in parts:
|
||||||
|
if p not in STOPWORDS and p not in toks:
|
||||||
|
toks.append(p)
|
||||||
|
if len(parts) > 1:
|
||||||
|
joined = "".join(parts)
|
||||||
|
if joined not in toks:
|
||||||
|
toks.append(joined)
|
||||||
|
return toks
|
||||||
|
|
||||||
|
|
||||||
|
SUBTEXT_RE = re.compile(r'^\s*(?:subtext|status):\s*qsTr\("([^"]+)"\)')
|
||||||
|
SECTION_RE = re.compile(r'^\s*SectionHeader\s*\{')
|
||||||
|
|
||||||
|
|
||||||
|
def extract_settings(files: dict[str, Path], nav: dict[str, dict]) -> list[dict]:
|
||||||
|
entries: list[dict] = []
|
||||||
|
for comp, meta in nav.items():
|
||||||
|
pf = files.get(comp)
|
||||||
|
if not pf:
|
||||||
|
continue
|
||||||
|
lines = read_lines(pf)
|
||||||
|
section = ""
|
||||||
|
i = 0
|
||||||
|
while i < len(lines):
|
||||||
|
if SECTION_RE.match(lines[i]):
|
||||||
|
for j in range(i + 1, min(i + 4, len(lines))):
|
||||||
|
m = LABEL_RE.match(lines[j])
|
||||||
|
if m:
|
||||||
|
section = m.group(1)
|
||||||
|
break
|
||||||
|
row_match = ROW_RE.match(lines[i])
|
||||||
|
if row_match:
|
||||||
|
row_type = row_match.group(1)
|
||||||
|
label = anchor = subtext = None
|
||||||
|
checked_path = toggled_path = None
|
||||||
|
for j in range(i + 1, min(i + 12, len(lines))):
|
||||||
|
if label is None:
|
||||||
|
m = LABEL_RE.match(lines[j])
|
||||||
|
if m:
|
||||||
|
label = m.group(1)
|
||||||
|
if anchor is None:
|
||||||
|
a = ANCHOR_RE.match(lines[j])
|
||||||
|
if a:
|
||||||
|
anchor = a.group(1)
|
||||||
|
if subtext is None:
|
||||||
|
st = SUBTEXT_RE.match(lines[j])
|
||||||
|
if st:
|
||||||
|
subtext = st.group(1)
|
||||||
|
if checked_path is None:
|
||||||
|
ch = CHECKED_RE.match(lines[j])
|
||||||
|
if ch:
|
||||||
|
checked_path = ch.group(1)
|
||||||
|
if toggled_path is None:
|
||||||
|
tg = ONTOGGLED_RE.match(lines[j])
|
||||||
|
if tg:
|
||||||
|
toggled_path = tg.group(1)
|
||||||
|
toggle_path = (
|
||||||
|
checked_path
|
||||||
|
if row_type == "ToggleRow" and checked_path and checked_path == toggled_path
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
if label and label not in SKIP_LABELS and anchor:
|
||||||
|
extra = " ".join(meta["crumbLabels"]) + \
|
||||||
|
" " + section + " " + (subtext or "")
|
||||||
|
entries.append({
|
||||||
|
"pageIdx": meta["pageIdx"], "subPath": meta["subPath"],
|
||||||
|
"crumbIcons": meta["crumbIcons"],
|
||||||
|
"crumbLabels": meta["crumbLabels"],
|
||||||
|
"title": label, "anchor": anchor,
|
||||||
|
"section": section,
|
||||||
|
"subtext": subtext or "",
|
||||||
|
"togglePath": toggle_path,
|
||||||
|
"keywords": " ".join(sorted(set(tokenize(label + " " + extra)))),
|
||||||
|
})
|
||||||
|
i += 1
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def build_inverted_and_ranking(entries: list[dict]):
|
||||||
|
inverted: dict[str, list[int]] = defaultdict(list)
|
||||||
|
ranking: dict[str, dict[int, float]] = defaultdict(dict)
|
||||||
|
for idx, e in enumerate(entries):
|
||||||
|
fields = {"title": e["title"], "keywords": e["keywords"]}
|
||||||
|
seen: set[str] = set()
|
||||||
|
for field, text in fields.items():
|
||||||
|
weight = FIELD_WEIGHT.get(field, 0.2)
|
||||||
|
for tok in tokenize(text):
|
||||||
|
if idx not in inverted[tok]:
|
||||||
|
inverted[tok].append(idx)
|
||||||
|
ranking[tok][idx] = max(ranking[tok].get(idx, 0.0), weight)
|
||||||
|
seen.add(tok)
|
||||||
|
for tok, ids in inverted.items():
|
||||||
|
ids.sort(key=lambda i: ranking[tok][i], reverse=True)
|
||||||
|
return inverted, {t: {str(k): v for k, v in d.items()} for t, d in ranking.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print(__doc__)
|
||||||
|
return 1
|
||||||
|
settings = Path(sys.argv[1])
|
||||||
|
out = Path(sys.argv[2])
|
||||||
|
files = discover_files(settings)
|
||||||
|
nav = build_nav_map(settings, files)
|
||||||
|
entries = extract_settings(files, nav)
|
||||||
|
inverted, ranking = build_inverted_and_ranking(entries)
|
||||||
|
for e in entries:
|
||||||
|
e.pop("keywords", None)
|
||||||
|
out.write_text(json.dumps({
|
||||||
|
"version": 2,
|
||||||
|
"entries": entries,
|
||||||
|
"inverted": inverted,
|
||||||
|
"ranking": ranking,
|
||||||
|
}, ensure_ascii=False, indent=2))
|
||||||
|
print(f"settings index: {len(entries)} entries, "
|
||||||
|
f"{len(inverted)} tokens -> {out}")
|
||||||
|
print("files:", len(files))
|
||||||
|
print("comps:", len(parse_page_comps(settings)))
|
||||||
|
print("registry:", len(parse_page_registry(settings)))
|
||||||
|
print("nav:", len(nav))
|
||||||
|
print("entries:", len(entries))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
BINARY="../Greeter/scripts/start-zshell-greeter"
|
BINARY="../Greeter/scripts/start-zshell-greeter"
|
||||||
CONFIG="../Greeter/scripts/zshell-hyprland.conf"
|
CONFIG="../Greeter/scripts/zshell-hyprland.lua"
|
||||||
GREETD_CONFIG="../Greeter/scripts/greeter-config.toml"
|
GREETD_CONFIG="../Greeter/scripts/greeter-config.toml"
|
||||||
WALLPAPER="$HOME/.local/state/zshell/lockscreen_bg.png"
|
WALLPAPER="$HOME/.local/state/zshell/lockscreen_bg.png"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user