mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-08 14:52:08 -04:00
qml: cut down on inline components for performance
This commit is contained in:
@@ -7,60 +7,8 @@ import Quickshell
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property Rounding rounding: Rounding {}
|
||||
readonly property Spacing spacing: Spacing {}
|
||||
readonly property FontSize fontSize: FontSize {}
|
||||
readonly property Anim anim: Anim {}
|
||||
|
||||
component Rounding: QtObject {
|
||||
readonly property int small: 8
|
||||
readonly property int normal: 12
|
||||
readonly property int large: 16
|
||||
readonly property int extraLarge: 24
|
||||
readonly property int full: 1000
|
||||
}
|
||||
|
||||
component Spacing: QtObject {
|
||||
readonly property int small: 4
|
||||
readonly property int normal: 8
|
||||
readonly property int large: 12
|
||||
readonly property int extraLarge: 16
|
||||
readonly property int huge: 24
|
||||
}
|
||||
|
||||
component FontSize: QtObject {
|
||||
readonly property int small: 12
|
||||
readonly property int normal: 14
|
||||
readonly property int large: 16
|
||||
readonly property int extraLarge: 20
|
||||
readonly property int huge: 24
|
||||
}
|
||||
|
||||
component AnimCurves: QtObject {
|
||||
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1
|
||||
/ 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
|
||||
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
|
||||
readonly property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
|
||||
}
|
||||
|
||||
component AnimDurations: QtObject {
|
||||
readonly property int quick: 150
|
||||
readonly property int normal: 300
|
||||
readonly property int slow: 500
|
||||
readonly property int extraSlow: 1000
|
||||
readonly property int expressiveFastSpatial: 350
|
||||
readonly property int expressiveDefaultSpatial: 500
|
||||
readonly property int expressiveEffects: 200
|
||||
}
|
||||
|
||||
component Anim: QtObject {
|
||||
readonly property AnimCurves curves: AnimCurves {}
|
||||
readonly property AnimDurations durations: AnimDurations {}
|
||||
}
|
||||
readonly property AppearanceRounding rounding: AppearanceRounding {}
|
||||
readonly property AppearanceSpacing spacing: AppearanceSpacing {}
|
||||
readonly property AppearanceFontSize fontSize: AppearanceFontSize {}
|
||||
readonly property AppearanceAnim anim: AppearanceAnim {}
|
||||
}
|
||||
|
||||
6
quickshell/Common/AppearanceAnim.qml
Normal file
6
quickshell/Common/AppearanceAnim.qml
Normal file
@@ -0,0 +1,6 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property AppearanceAnimCurves curves: AppearanceAnimCurves {}
|
||||
readonly property AppearanceAnimDurations durations: AppearanceAnimDurations {}
|
||||
}
|
||||
13
quickshell/Common/AppearanceAnimCurves.qml
Normal file
13
quickshell/Common/AppearanceAnimCurves.qml
Normal file
@@ -0,0 +1,13 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
|
||||
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
|
||||
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
|
||||
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1]
|
||||
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1]
|
||||
readonly property list<real> expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1]
|
||||
}
|
||||
11
quickshell/Common/AppearanceAnimDurations.qml
Normal file
11
quickshell/Common/AppearanceAnimDurations.qml
Normal file
@@ -0,0 +1,11 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int quick: 150
|
||||
readonly property int normal: 300
|
||||
readonly property int slow: 500
|
||||
readonly property int extraSlow: 1000
|
||||
readonly property int expressiveFastSpatial: 350
|
||||
readonly property int expressiveDefaultSpatial: 500
|
||||
readonly property int expressiveEffects: 200
|
||||
}
|
||||
9
quickshell/Common/AppearanceFontSize.qml
Normal file
9
quickshell/Common/AppearanceFontSize.qml
Normal file
@@ -0,0 +1,9 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int small: 12
|
||||
readonly property int normal: 14
|
||||
readonly property int large: 16
|
||||
readonly property int extraLarge: 20
|
||||
readonly property int huge: 24
|
||||
}
|
||||
9
quickshell/Common/AppearanceRounding.qml
Normal file
9
quickshell/Common/AppearanceRounding.qml
Normal file
@@ -0,0 +1,9 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int small: 8
|
||||
readonly property int normal: 12
|
||||
readonly property int large: 16
|
||||
readonly property int extraLarge: 24
|
||||
readonly property int full: 1000
|
||||
}
|
||||
9
quickshell/Common/AppearanceSpacing.qml
Normal file
9
quickshell/Common/AppearanceSpacing.qml
Normal file
@@ -0,0 +1,9 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int small: 4
|
||||
readonly property int normal: 8
|
||||
readonly property int large: 12
|
||||
readonly property int extraLarge: 16
|
||||
readonly property int huge: 24
|
||||
}
|
||||
54
quickshell/Modals/WindowRuleCheckboxRow.qml
Normal file
54
quickshell/Modals/WindowRuleCheckboxRow.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: checkboxRow
|
||||
|
||||
property alias checked: checkbox.checked
|
||||
property alias label: labelText.text
|
||||
property bool indeterminate: false
|
||||
|
||||
spacing: Theme.spacingS
|
||||
height: 24
|
||||
|
||||
Rectangle {
|
||||
id: checkbox
|
||||
property bool checked: false
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 4
|
||||
color: checkboxRow.indeterminate ? Theme.surfaceVariant : (checked ? Theme.primary : "transparent")
|
||||
border.color: checkboxRow.indeterminate ? Theme.outlineButton : (checked ? Theme.primary : Theme.outlineButton)
|
||||
border.width: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: checkboxRow.indeterminate ? "remove" : "check"
|
||||
size: 12
|
||||
color: checkboxRow.indeterminate ? Theme.surfaceVariantText : Theme.background
|
||||
visible: parent.checked || checkboxRow.indeterminate
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (checkboxRow.indeterminate) {
|
||||
checkboxRow.indeterminate = false;
|
||||
checkbox.checked = true;
|
||||
} else {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: labelText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
17
quickshell/Modals/WindowRuleInputField.qml
Normal file
17
quickshell/Modals/WindowRuleInputField.qml
Normal file
@@ -0,0 +1,17 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Rectangle {
|
||||
id: inputFieldRect
|
||||
|
||||
default property alias contentData: inputFieldRect.data
|
||||
property bool hasFocus: false
|
||||
property int fieldHeight: Theme.fontSizeMedium + Theme.spacingL * 2
|
||||
|
||||
width: parent.width
|
||||
height: fieldHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceHover
|
||||
border.color: hasFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: hasFocus ? 2 : 1
|
||||
}
|
||||
@@ -297,78 +297,6 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
component SectionHeader: StyledText {
|
||||
property string title
|
||||
text: title
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
topPadding: Theme.spacingM
|
||||
bottomPadding: Theme.spacingXS
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
component CheckboxRow: Row {
|
||||
property alias checked: checkbox.checked
|
||||
property alias label: labelText.text
|
||||
property bool indeterminate: false
|
||||
spacing: Theme.spacingS
|
||||
height: 24
|
||||
|
||||
Rectangle {
|
||||
id: checkbox
|
||||
property bool checked: false
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 4
|
||||
color: parent.indeterminate ? Theme.surfaceVariant : (checked ? Theme.primary : "transparent")
|
||||
border.color: parent.indeterminate ? Theme.outlineButton : (checked ? Theme.primary : Theme.outlineButton)
|
||||
border.width: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: parent.parent.indeterminate ? "remove" : "check"
|
||||
size: 12
|
||||
color: parent.parent.indeterminate ? Theme.surfaceVariantText : Theme.background
|
||||
visible: parent.checked || parent.parent.indeterminate
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (parent.parent.indeterminate) {
|
||||
parent.parent.indeterminate = false;
|
||||
parent.checked = true;
|
||||
} else {
|
||||
parent.checked = !parent.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: labelText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
component InputField: Rectangle {
|
||||
id: inputFieldRect
|
||||
default property alias contentData: inputFieldRect.data
|
||||
property bool hasFocus: false
|
||||
width: parent.width
|
||||
height: root.inputFieldHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceHover
|
||||
border.color: hasFocus ? Theme.primary : Theme.outlineStrong
|
||||
border.width: hasFocus ? 2 : 1
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
@@ -447,7 +375,7 @@ FloatingWindow {
|
||||
width: flickable.width - Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
hasFocus: nameInput.activeFocus
|
||||
DankTextField {
|
||||
id: nameInput
|
||||
@@ -460,11 +388,11 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
WindowRuleSectionHeader {
|
||||
title: I18n.tr("Match Criteria")
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
hasFocus: appIdInput.activeFocus
|
||||
DankTextField {
|
||||
id: appIdInput
|
||||
@@ -481,7 +409,7 @@ FloatingWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: addTitleBtn.visible ? parent.width - addTitleBtn.width - Theme.spacingS : parent.width
|
||||
hasFocus: titleInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -514,7 +442,7 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
WindowRuleSectionHeader {
|
||||
title: I18n.tr("Window Opening")
|
||||
}
|
||||
|
||||
@@ -522,24 +450,24 @@ FloatingWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingL
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: floatingToggle
|
||||
label: I18n.tr("Float")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: maximizedToggle
|
||||
label: I18n.tr("Maximize")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: fullscreenToggle
|
||||
label: I18n.tr("Fullscreen")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: maximizedToEdgesToggle
|
||||
label: I18n.tr("Max to Edges")
|
||||
visible: isNiri
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: openFocusedToggle
|
||||
label: I18n.tr("Focus")
|
||||
visible: isNiri
|
||||
@@ -563,7 +491,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: outputInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -590,7 +518,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: workspaceInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -623,7 +551,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: columnWidthInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -650,7 +578,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: windowHeightInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -666,7 +594,7 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
WindowRuleSectionHeader {
|
||||
title: I18n.tr("Dynamic Properties")
|
||||
}
|
||||
|
||||
@@ -674,7 +602,7 @@ FloatingWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: opacityEnabled
|
||||
label: I18n.tr("Opacity")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -696,19 +624,19 @@ FloatingWindow {
|
||||
spacing: Theme.spacingL
|
||||
visible: isNiri
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: vrrToggle
|
||||
label: I18n.tr("VRR On-Demand")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: clipToGeometryToggle
|
||||
label: I18n.tr("Clip to Geometry")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: tiledStateToggle
|
||||
label: I18n.tr("Tiled State")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: drawBorderBgToggle
|
||||
label: I18n.tr("Border with BG")
|
||||
}
|
||||
@@ -769,7 +697,7 @@ FloatingWindow {
|
||||
spacing: Theme.spacingM
|
||||
visible: isNiri
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: scrollFactorEnabled
|
||||
label: I18n.tr("Scroll Factor")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -790,7 +718,7 @@ FloatingWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: cornerRadiusEnabled
|
||||
label: I18n.tr("Corner Radius")
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -807,7 +735,7 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
WindowRuleSectionHeader {
|
||||
title: I18n.tr("Size Constraints")
|
||||
}
|
||||
|
||||
@@ -827,7 +755,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: minWidthInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -854,7 +782,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: maxWidthInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -881,7 +809,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: minHeightInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -908,7 +836,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: maxHeightInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -924,7 +852,7 @@ FloatingWindow {
|
||||
}
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
WindowRuleSectionHeader {
|
||||
title: I18n.tr("Hyprland Options")
|
||||
visible: isHyprland
|
||||
}
|
||||
@@ -934,43 +862,43 @@ FloatingWindow {
|
||||
spacing: Theme.spacingL
|
||||
visible: isHyprland
|
||||
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: tileToggle
|
||||
label: I18n.tr("Tile")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noFocusToggle
|
||||
label: I18n.tr("No Focus")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noBorderToggle
|
||||
label: I18n.tr("No Border")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noShadowToggle
|
||||
label: I18n.tr("No Shadow")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noDimToggle
|
||||
label: I18n.tr("No Dim")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noBlurToggle
|
||||
label: I18n.tr("No Blur")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noAnimToggle
|
||||
label: I18n.tr("No Anim")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: noRoundingToggle
|
||||
label: I18n.tr("No Rounding")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: pinToggle
|
||||
label: I18n.tr("Pin")
|
||||
}
|
||||
CheckboxRow {
|
||||
WindowRuleCheckboxRow {
|
||||
id: opaqueToggle
|
||||
label: I18n.tr("Opaque")
|
||||
}
|
||||
@@ -993,7 +921,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: sizeInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -1020,7 +948,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: moveInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -1053,7 +981,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: monitorInput.activeFocus
|
||||
DankTextField {
|
||||
@@ -1080,7 +1008,7 @@ FloatingWindow {
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
|
||||
InputField {
|
||||
WindowRuleInputField {
|
||||
width: parent.width
|
||||
hasFocus: hyprWorkspaceInput.activeFocus
|
||||
DankTextField {
|
||||
|
||||
15
quickshell/Modals/WindowRuleSectionHeader.qml
Normal file
15
quickshell/Modals/WindowRuleSectionHeader.qml
Normal file
@@ -0,0 +1,15 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
StyledText {
|
||||
property string title
|
||||
text: title
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
topPadding: Theme.spacingM
|
||||
bottomPadding: Theme.spacingXS
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
}
|
||||
161
quickshell/Modules/ProcessList/CircleGauge.qml
Normal file
161
quickshell/Modules/ProcessList/CircleGauge.qml
Normal file
@@ -0,0 +1,161 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: gaugeRoot
|
||||
|
||||
property real value: 0
|
||||
property string label: ""
|
||||
property string sublabel: ""
|
||||
property string detail: ""
|
||||
property color accentColor: Theme.primary
|
||||
property color detailColor: Theme.surfaceVariantText
|
||||
|
||||
readonly property real thickness: Math.max(4, Math.min(width, height) / 15)
|
||||
readonly property real glowExtra: thickness * 1.4
|
||||
readonly property real arcPadding: (thickness + glowExtra) / 2
|
||||
|
||||
readonly property real innerDiameter: width - (arcPadding + thickness + glowExtra) * 2
|
||||
readonly property real maxTextWidth: innerDiameter * 0.9
|
||||
readonly property real baseLabelSize: Math.round(width * 0.18)
|
||||
readonly property real labelSize: Math.round(Math.min(baseLabelSize, maxTextWidth / Math.max(1, label.length * 0.65)))
|
||||
readonly property real sublabelSize: Math.round(Math.min(width * 0.13, maxTextWidth / Math.max(1, sublabel.length * 0.7)))
|
||||
readonly property real detailSize: Math.round(Math.min(width * 0.12, maxTextWidth / Math.max(1, detail.length * 0.65)))
|
||||
|
||||
property real animValue: 0
|
||||
|
||||
onValueChanged: animValue = Math.min(1, Math.max(0, value))
|
||||
|
||||
Behavior on animValue {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: animValue = Math.min(1, Math.max(0, value))
|
||||
|
||||
Canvas {
|
||||
id: glowCanvas
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||
const startAngle = -Math.PI * 0.5;
|
||||
const endAngle = Math.PI * 1.5;
|
||||
|
||||
ctx.lineCap = "round";
|
||||
|
||||
if (gaugeRoot.animValue > 0) {
|
||||
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.2);
|
||||
ctx.lineWidth = gaugeRoot.thickness + gaugeRoot.glowExtra;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: gaugeRoot
|
||||
function onAnimValueChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onAccentColorChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onWidthChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: arcCanvas
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||
const startAngle = -Math.PI * 0.5;
|
||||
const endAngle = Math.PI * 1.5;
|
||||
|
||||
ctx.lineCap = "round";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, endAngle);
|
||||
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.1);
|
||||
ctx.lineWidth = gaugeRoot.thickness;
|
||||
ctx.stroke();
|
||||
|
||||
if (gaugeRoot.animValue > 0) {
|
||||
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||
ctx.strokeStyle = gaugeRoot.accentColor;
|
||||
ctx.lineWidth = gaugeRoot.thickness;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: gaugeRoot
|
||||
function onAnimValueChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onAccentColorChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onWidthChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.label
|
||||
font.pixelSize: gaugeRoot.labelSize
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.sublabel
|
||||
font.pixelSize: gaugeRoot.sublabelSize
|
||||
font.weight: Font.Medium
|
||||
color: gaugeRoot.accentColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.detail
|
||||
font.pixelSize: gaugeRoot.detailSize
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: gaugeRoot.detailColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: gaugeRoot.detail.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
29
quickshell/Modules/ProcessList/InfoRow.qml
Normal file
29
quickshell/Modules/ProcessList/InfoRow.qml
Normal file
@@ -0,0 +1,29 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
RowLayout {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: label + ":"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceVariantText
|
||||
Layout.preferredWidth: 100
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: value
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
160
quickshell/Modules/ProcessList/PerformanceCard.qml
Normal file
160
quickshell/Modules/ProcessList/PerformanceCard.qml
Normal file
@@ -0,0 +1,160 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: card
|
||||
|
||||
property string title: ""
|
||||
property string icon: ""
|
||||
property string value: ""
|
||||
property string subtitle: ""
|
||||
property color accentColor: Theme.primary
|
||||
property var history: []
|
||||
property var history2: null
|
||||
property real maxValue: 100
|
||||
property bool showSecondary: false
|
||||
property string extraInfo: ""
|
||||
property color extraInfoColor: Theme.surfaceVariantText
|
||||
property int historySize: 60
|
||||
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
|
||||
Canvas {
|
||||
id: graphCanvas
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
property var hist: card.history
|
||||
property var hist2: card.history2
|
||||
|
||||
onHistChanged: requestPaint()
|
||||
onHist2Changed: requestPaint()
|
||||
onWidthChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
if (!hist || hist.length < 2)
|
||||
return;
|
||||
|
||||
let max = card.maxValue;
|
||||
if (max <= 0) {
|
||||
max = 1;
|
||||
for (let k = 0; k < hist.length; k++)
|
||||
max = Math.max(max, hist[k]);
|
||||
if (hist2) {
|
||||
for (let l = 0; l < hist2.length; l++)
|
||||
max = Math.max(max, hist2[l]);
|
||||
}
|
||||
max *= 1.1;
|
||||
}
|
||||
|
||||
const c = card.accentColor;
|
||||
const grad = ctx.createLinearGradient(0, 0, 0, height);
|
||||
grad.addColorStop(0, Qt.rgba(c.r, c.g, c.b, 0.25));
|
||||
grad.addColorStop(1, Qt.rgba(c.r, c.g, c.b, 0.02));
|
||||
|
||||
ctx.fillStyle = grad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, height);
|
||||
for (let i = 0; i < hist.length; i++) {
|
||||
const x = (width / (card.historySize - 1)) * i;
|
||||
const y = height - (hist[i] / max) * height * 0.8;
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.lineTo((width / (card.historySize - 1)) * (hist.length - 1), height);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.8);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
for (let j = 0; j < hist.length; j++) {
|
||||
const px = (width / (card.historySize - 1)) * j;
|
||||
const py = height - (hist[j] / max) * height * 0.8;
|
||||
j === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
if (hist2 && hist2.length >= 2 && card.showSecondary) {
|
||||
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.4);
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.beginPath();
|
||||
for (let m = 0; m < hist2.length; m++) {
|
||||
const sx = (width / (card.historySize - 1)) * m;
|
||||
const sy = height - (hist2[m] / max) * height * 0.8;
|
||||
m === 0 ? ctx.moveTo(sx, sy) : ctx.lineTo(sx, sy);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: card.icon
|
||||
size: Theme.iconSize
|
||||
color: card.accentColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.title
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.extraInfo
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: card.extraInfoColor
|
||||
visible: card.extraInfo.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.value
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.subtitle
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -73,6 +72,7 @@ Item {
|
||||
PerformanceCard {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
historySize: root.historySize
|
||||
title: "CPU"
|
||||
icon: "memory"
|
||||
value: DgopService.cpuUsage.toFixed(1) + "%"
|
||||
@@ -88,6 +88,7 @@ Item {
|
||||
PerformanceCard {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
historySize: root.historySize
|
||||
title: I18n.tr("Memory")
|
||||
icon: "sd_card"
|
||||
value: DgopService.memoryUsage.toFixed(1) + "%"
|
||||
@@ -109,6 +110,7 @@ Item {
|
||||
PerformanceCard {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
historySize: root.historySize
|
||||
title: I18n.tr("Network")
|
||||
icon: "swap_horiz"
|
||||
value: "↓ " + root.formatBytes(DgopService.networkRxRate)
|
||||
@@ -125,6 +127,7 @@ Item {
|
||||
PerformanceCard {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
historySize: root.historySize
|
||||
title: I18n.tr("Disk")
|
||||
icon: "storage"
|
||||
value: "R: " + root.formatBytes(DgopService.diskReadRate)
|
||||
@@ -146,159 +149,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PerformanceCard: Rectangle {
|
||||
id: card
|
||||
|
||||
property string title: ""
|
||||
property string icon: ""
|
||||
property string value: ""
|
||||
property string subtitle: ""
|
||||
property color accentColor: Theme.primary
|
||||
property var history: []
|
||||
property var history2: null
|
||||
property real maxValue: 100
|
||||
property bool showSecondary: false
|
||||
property string extraInfo: ""
|
||||
property color extraInfoColor: Theme.surfaceVariantText
|
||||
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
|
||||
Canvas {
|
||||
id: graphCanvas
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
property var hist: card.history
|
||||
property var hist2: card.history2
|
||||
|
||||
onHistChanged: requestPaint()
|
||||
onHist2Changed: requestPaint()
|
||||
onWidthChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
if (!hist || hist.length < 2)
|
||||
return;
|
||||
|
||||
let max = card.maxValue;
|
||||
if (max <= 0) {
|
||||
max = 1;
|
||||
for (let k = 0; k < hist.length; k++)
|
||||
max = Math.max(max, hist[k]);
|
||||
if (hist2) {
|
||||
for (let l = 0; l < hist2.length; l++)
|
||||
max = Math.max(max, hist2[l]);
|
||||
}
|
||||
max *= 1.1;
|
||||
}
|
||||
|
||||
const c = card.accentColor;
|
||||
const grad = ctx.createLinearGradient(0, 0, 0, height);
|
||||
grad.addColorStop(0, Qt.rgba(c.r, c.g, c.b, 0.25));
|
||||
grad.addColorStop(1, Qt.rgba(c.r, c.g, c.b, 0.02));
|
||||
|
||||
ctx.fillStyle = grad;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, height);
|
||||
for (let i = 0; i < hist.length; i++) {
|
||||
const x = (width / (root.historySize - 1)) * i;
|
||||
const y = height - (hist[i] / max) * height * 0.8;
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.lineTo((width / (root.historySize - 1)) * (hist.length - 1), height);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.8);
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
for (let j = 0; j < hist.length; j++) {
|
||||
const px = (width / (root.historySize - 1)) * j;
|
||||
const py = height - (hist[j] / max) * height * 0.8;
|
||||
j === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
if (hist2 && hist2.length >= 2 && card.showSecondary) {
|
||||
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.4);
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.setLineDash([4, 4]);
|
||||
ctx.beginPath();
|
||||
for (let m = 0; m < hist2.length; m++) {
|
||||
const sx = (width / (root.historySize - 1)) * m;
|
||||
const sy = height - (hist2[m] / max) * height * 0.8;
|
||||
m === 0 ? ctx.moveTo(sx, sy) : ctx.lineTo(sx, sy);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: card.icon
|
||||
size: Theme.iconSize
|
||||
color: card.accentColor
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.title
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.extraInfo
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: card.extraInfoColor
|
||||
visible: card.extraInfo.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.value
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: card.subtitle
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
334
quickshell/Modules/ProcessList/ProcessItem.qml
Normal file
334
quickshell/Modules/ProcessList/ProcessItem.qml
Normal file
@@ -0,0 +1,334 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: processItemRoot
|
||||
|
||||
property var process: null
|
||||
property bool isExpanded: false
|
||||
property bool isSelected: false
|
||||
property var contextMenu: null
|
||||
|
||||
signal toggleExpand
|
||||
signal clicked
|
||||
signal contextMenuRequested(real mouseX, real mouseY)
|
||||
|
||||
readonly property int processPid: process?.pid ?? 0
|
||||
readonly property real processCpu: process?.cpu ?? 0
|
||||
readonly property int processMemKB: process?.memoryKB ?? 0
|
||||
readonly property string processCmd: process?.command ?? ""
|
||||
readonly property string processFullCmd: process?.fullCommand ?? processCmd
|
||||
|
||||
height: isExpanded ? (44 + expandedRect.height + Theme.spacingXS) : 44
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (isSelected)
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15);
|
||||
return processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent";
|
||||
}
|
||||
border.color: {
|
||||
if (isSelected)
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
|
||||
return processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent";
|
||||
}
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: processMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
processItemRoot.contextMenuRequested(mouse.x, mouse.y);
|
||||
return;
|
||||
}
|
||||
processItemRoot.clicked();
|
||||
processItemRoot.toggleExpand();
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 44
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 200
|
||||
height: parent.height
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: DgopService.getProcessIcon(processItemRoot.processCmd)
|
||||
size: Theme.iconSize - 4
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
opacity: 0.8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: processItemRoot.processCmd
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
width: Math.min(implicitWidth, 280)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 70
|
||||
height: 24
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: DgopService.formatCpuUsage(processItemRoot.processCpu)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 70
|
||||
height: 24
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: DgopService.formatMemoryUsage(processItemRoot.processMemKB)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: {
|
||||
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 80
|
||||
height: parent.height
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: processItemRoot.processPid > 0 ? processItemRoot.processPid.toString() : ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 40
|
||||
height: parent.height
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: processItemRoot.isExpanded ? "expand_less" : "expand_more"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: expandedRect
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
height: processItemRoot.isExpanded ? (expandedContent.implicitHeight + Theme.spacingS * 2) : 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
radius: Theme.cornerRadius - 2
|
||||
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, 0.6)
|
||||
clip: true
|
||||
visible: processItemRoot.isExpanded
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: expandedContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
id: cmdLabel
|
||||
text: I18n.tr("Full Command:", "process detail label")
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: cmdText
|
||||
text: processItemRoot.processFullCmd
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: copyBtn
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: Theme.cornerRadius - 2
|
||||
color: copyMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) : "transparent"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "content_copy"
|
||||
size: 14
|
||||
color: copyMouseArea.containsMouse ? Theme.primary : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: copyMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["dms", "cl", "copy", processItemRoot.processFullCmd]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "PPID:"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (processItemRoot.process?.ppid ?? 0) > 0 ? processItemRoot.process.ppid.toString() : "--"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Mem:"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (processItemRoot.process?.memoryPercent ?? 0).toFixed(1) + "%"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,162 +374,4 @@ DankPopout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component CircleGauge: Item {
|
||||
id: gaugeRoot
|
||||
|
||||
property real value: 0
|
||||
property string label: ""
|
||||
property string sublabel: ""
|
||||
property string detail: ""
|
||||
property color accentColor: Theme.primary
|
||||
property color detailColor: Theme.surfaceVariantText
|
||||
|
||||
readonly property real thickness: Math.max(4, Math.min(width, height) / 15)
|
||||
readonly property real glowExtra: thickness * 1.4
|
||||
readonly property real arcPadding: (thickness + glowExtra) / 2
|
||||
|
||||
readonly property real innerDiameter: width - (arcPadding + thickness + glowExtra) * 2
|
||||
readonly property real maxTextWidth: innerDiameter * 0.9
|
||||
readonly property real baseLabelSize: Math.round(width * 0.18)
|
||||
readonly property real labelSize: Math.round(Math.min(baseLabelSize, maxTextWidth / Math.max(1, label.length * 0.65)))
|
||||
readonly property real sublabelSize: Math.round(Math.min(width * 0.13, maxTextWidth / Math.max(1, sublabel.length * 0.7)))
|
||||
readonly property real detailSize: Math.round(Math.min(width * 0.12, maxTextWidth / Math.max(1, detail.length * 0.65)))
|
||||
|
||||
property real animValue: 0
|
||||
|
||||
onValueChanged: animValue = Math.min(1, Math.max(0, value))
|
||||
|
||||
Behavior on animValue {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: animValue = Math.min(1, Math.max(0, value))
|
||||
|
||||
Canvas {
|
||||
id: glowCanvas
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||
const startAngle = -Math.PI * 0.5;
|
||||
const endAngle = Math.PI * 1.5;
|
||||
|
||||
ctx.lineCap = "round";
|
||||
|
||||
if (gaugeRoot.animValue > 0) {
|
||||
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.2);
|
||||
ctx.lineWidth = gaugeRoot.thickness + gaugeRoot.glowExtra;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: gaugeRoot
|
||||
function onAnimValueChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onAccentColorChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onWidthChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
glowCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: arcCanvas
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||
const startAngle = -Math.PI * 0.5;
|
||||
const endAngle = Math.PI * 1.5;
|
||||
|
||||
ctx.lineCap = "round";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, endAngle);
|
||||
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.1);
|
||||
ctx.lineWidth = gaugeRoot.thickness;
|
||||
ctx.stroke();
|
||||
|
||||
if (gaugeRoot.animValue > 0) {
|
||||
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||
ctx.strokeStyle = gaugeRoot.accentColor;
|
||||
ctx.lineWidth = gaugeRoot.thickness;
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: gaugeRoot
|
||||
function onAnimValueChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onAccentColorChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onWidthChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
arcCanvas.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.label
|
||||
font.pixelSize: gaugeRoot.labelSize
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.sublabel
|
||||
font.pixelSize: gaugeRoot.sublabelSize
|
||||
font.weight: Font.Medium
|
||||
color: gaugeRoot.accentColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: gaugeRoot.detail
|
||||
font.pixelSize: gaugeRoot.detailSize
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: gaugeRoot.detailColor
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: gaugeRoot.detail.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,402 +368,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SortableHeader: Item {
|
||||
id: headerItem
|
||||
|
||||
property string text: ""
|
||||
property string sortKey: ""
|
||||
property string currentSort: ""
|
||||
property bool sortAscending: false
|
||||
property int alignment: Text.AlignHCenter
|
||||
|
||||
signal clicked
|
||||
|
||||
readonly property bool isActive: sortKey === currentSort
|
||||
|
||||
height: 36
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
radius: Theme.cornerRadius
|
||||
color: headerItem.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (headerMouseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06) : "transparent")
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: 4
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: headerItem.alignment === Text.AlignLeft
|
||||
visible: headerItem.alignment !== Text.AlignLeft
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: headerItem.text
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: headerItem.isActive ? Font.Bold : Font.Medium
|
||||
color: headerItem.isActive ? Theme.primary : Theme.surfaceText
|
||||
opacity: headerItem.isActive ? 1 : 0.8
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: headerItem.sortAscending ? "arrow_upward" : "arrow_downward"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
visible: headerItem.isActive
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: headerItem.alignment !== Text.AlignLeft
|
||||
visible: headerItem.alignment === Text.AlignLeft
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: headerItem.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
component ProcessItem: Rectangle {
|
||||
id: processItemRoot
|
||||
|
||||
property var process: null
|
||||
property bool isExpanded: false
|
||||
property bool isSelected: false
|
||||
property var contextMenu: null
|
||||
|
||||
signal toggleExpand
|
||||
signal clicked
|
||||
signal contextMenuRequested(real mouseX, real mouseY)
|
||||
|
||||
readonly property int processPid: process?.pid ?? 0
|
||||
readonly property real processCpu: process?.cpu ?? 0
|
||||
readonly property int processMemKB: process?.memoryKB ?? 0
|
||||
readonly property string processCmd: process?.command ?? ""
|
||||
readonly property string processFullCmd: process?.fullCommand ?? processCmd
|
||||
|
||||
height: isExpanded ? (44 + expandedRect.height + Theme.spacingXS) : 44
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (isSelected)
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15);
|
||||
return processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent";
|
||||
}
|
||||
border.color: {
|
||||
if (isSelected)
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
|
||||
return processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent";
|
||||
}
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: processMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
onClicked: mouse => {
|
||||
if (mouse.button === Qt.RightButton) {
|
||||
processItemRoot.contextMenuRequested(mouse.x, mouse.y);
|
||||
return;
|
||||
}
|
||||
processItemRoot.clicked();
|
||||
processItemRoot.toggleExpand();
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 44
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 200
|
||||
height: parent.height
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: DgopService.getProcessIcon(processItemRoot.processCmd)
|
||||
size: Theme.iconSize - 4
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
opacity: 0.8
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: processItemRoot.processCmd
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
width: Math.min(implicitWidth, 280)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 70
|
||||
height: 24
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: DgopService.formatCpuUsage(processItemRoot.processCpu)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: {
|
||||
if (processItemRoot.processCpu > 80)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processCpu > 50)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 100
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: 70
|
||||
height: 24
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: DgopService.formatMemoryUsage(processItemRoot.processMemKB)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: Font.Bold
|
||||
color: {
|
||||
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||
return Theme.error;
|
||||
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||
return Theme.warning;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 80
|
||||
height: parent.height
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: processItemRoot.processPid > 0 ? processItemRoot.processPid.toString() : ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 40
|
||||
height: parent.height
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: processItemRoot.isExpanded ? "expand_less" : "expand_more"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: expandedRect
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
height: processItemRoot.isExpanded ? (expandedContent.implicitHeight + Theme.spacingS * 2) : 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
radius: Theme.cornerRadius - 2
|
||||
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, 0.6)
|
||||
clip: true
|
||||
visible: processItemRoot.isExpanded
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: expandedContent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
id: cmdLabel
|
||||
text: I18n.tr("Full Command:", "process detail label")
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: cmdText
|
||||
text: processItemRoot.processFullCmd
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: copyBtn
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: Theme.cornerRadius - 2
|
||||
color: copyMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) : "transparent"
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "content_copy"
|
||||
size: 14
|
||||
color: copyMouseArea.containsMouse ? Theme.primary : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: copyMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["dms", "cl", "copy", processItemRoot.processFullCmd]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "PPID:"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (processItemRoot.process?.ppid ?? 0) > 0 ? processItemRoot.process.ppid.toString() : "--"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Mem:"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.weight: Font.Bold
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (processItemRoot.process?.memoryPercent ?? 0).toFixed(1) + "%"
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
74
quickshell/Modules/ProcessList/SortableHeader.qml
Normal file
74
quickshell/Modules/ProcessList/SortableHeader.qml
Normal file
@@ -0,0 +1,74 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: headerItem
|
||||
|
||||
property string text: ""
|
||||
property string sortKey: ""
|
||||
property string currentSort: ""
|
||||
property bool sortAscending: false
|
||||
property int alignment: Text.AlignHCenter
|
||||
|
||||
signal clicked
|
||||
|
||||
readonly property bool isActive: sortKey === currentSort
|
||||
|
||||
height: 36
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
radius: Theme.cornerRadius
|
||||
color: headerItem.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (headerMouseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06) : "transparent")
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: 4
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: headerItem.alignment === Text.AlignLeft
|
||||
visible: headerItem.alignment !== Text.AlignLeft
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: headerItem.text
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
font.weight: headerItem.isActive ? Font.Bold : Font.Medium
|
||||
color: headerItem.isActive ? Theme.primary : Theme.surfaceText
|
||||
opacity: headerItem.isActive ? 1 : 0.8
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: headerItem.sortAscending ? "arrow_upward" : "arrow_downward"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
visible: headerItem.isActive
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: headerItem.alignment !== Text.AlignLeft
|
||||
visible: headerItem.alignment === Text.AlignLeft
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: headerItem.clicked()
|
||||
}
|
||||
}
|
||||
@@ -358,29 +358,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component InfoRow: RowLayout {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: label + ":"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceVariantText
|
||||
Layout.preferredWidth: 100
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: value
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.family: SettingsData.monoFontFamily
|
||||
color: Theme.surfaceText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
147
quickshell/Services/NotifWrapper.qml
Normal file
147
quickshell/Services/NotifWrapper.qml
Normal file
@@ -0,0 +1,147 @@
|
||||
import QtQuick
|
||||
import Quickshell.Services.Notifications
|
||||
import qs.Common
|
||||
|
||||
QtObject {
|
||||
id: wrapper
|
||||
|
||||
property bool popup: false
|
||||
property bool removedByLimit: false
|
||||
property bool isPersistent: true
|
||||
property int seq: 0
|
||||
property string persistedImagePath: ""
|
||||
|
||||
onPopupChanged: {
|
||||
if (!popup) {
|
||||
NotificationService.removeFromVisibleNotifications(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Timer timer: Timer {
|
||||
interval: {
|
||||
if (!wrapper.notification)
|
||||
return 5000;
|
||||
switch (wrapper.urgency) {
|
||||
case NotificationUrgency.Low:
|
||||
return SettingsData.notificationTimeoutLow;
|
||||
case NotificationUrgency.Critical:
|
||||
return SettingsData.notificationTimeoutCritical;
|
||||
default:
|
||||
return SettingsData.notificationTimeoutNormal;
|
||||
}
|
||||
}
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (interval > 0) {
|
||||
wrapper.popup = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property date time: new Date()
|
||||
readonly property string timeStr: {
|
||||
NotificationService.timeUpdateTick;
|
||||
NotificationService.clockFormatChanged;
|
||||
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - time.getTime();
|
||||
const minutes = Math.floor(diff / 60000);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
|
||||
if (hours < 1) {
|
||||
if (minutes < 1) {
|
||||
return "now";
|
||||
}
|
||||
return `${minutes}m ago`;
|
||||
}
|
||||
|
||||
const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate());
|
||||
const daysDiff = Math.floor((nowDate - timeDate) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysDiff === 0) {
|
||||
return formatTime(time);
|
||||
}
|
||||
|
||||
try {
|
||||
const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US";
|
||||
const weekday = time.toLocaleDateString(localeName, {
|
||||
weekday: "long"
|
||||
});
|
||||
return `${weekday}, ${formatTime(time)}`;
|
||||
} catch (e) {
|
||||
return formatTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(date) {
|
||||
let use24Hour = true;
|
||||
try {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.use24HourClock !== undefined) {
|
||||
use24Hour = SettingsData.use24HourClock;
|
||||
}
|
||||
} catch (e) {
|
||||
use24Hour = true;
|
||||
}
|
||||
|
||||
if (use24Hour) {
|
||||
return date.toLocaleTimeString(Qt.locale(), "HH:mm");
|
||||
} else {
|
||||
return date.toLocaleTimeString(Qt.locale(), "h:mm AP");
|
||||
}
|
||||
}
|
||||
|
||||
required property Notification notification
|
||||
readonly property string summary: (notification?.summary ?? "").replace(/<img\b[^>]*>/gi, "")
|
||||
readonly property string body: (notification?.body ?? "").replace(/<img\b[^>]*>/gi, "")
|
||||
readonly property string htmlBody: NotificationService._resolveHtmlBody(body)
|
||||
readonly property string appIcon: notification?.appIcon ?? ""
|
||||
readonly property string appName: {
|
||||
if (!notification)
|
||||
return "app";
|
||||
if (notification.appName == "") {
|
||||
const entry = DesktopEntries.heuristicLookup(notification.desktopEntry);
|
||||
if (entry && entry.name)
|
||||
return entry.name.toLowerCase();
|
||||
}
|
||||
return notification.appName || "app";
|
||||
}
|
||||
readonly property string desktopEntry: notification?.desktopEntry ?? ""
|
||||
readonly property string image: notification?.image ?? ""
|
||||
readonly property string cleanImage: {
|
||||
if (!image)
|
||||
return "";
|
||||
return Paths.strip(image);
|
||||
}
|
||||
property int urgencyOverride: notification?.urgency ?? NotificationUrgency.Normal
|
||||
readonly property int urgency: urgencyOverride
|
||||
readonly property list<NotificationAction> actions: notification?.actions ?? []
|
||||
|
||||
readonly property Connections conn: Connections {
|
||||
target: wrapper.notification?.Retainable ?? null
|
||||
|
||||
function onDropped(): void {
|
||||
NotificationService.allWrappers = NotificationService.allWrappers.filter(w => w !== wrapper);
|
||||
NotificationService.notifications = NotificationService.notifications.filter(w => w !== wrapper);
|
||||
|
||||
if (NotificationService.bulkDismissing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupKey = NotificationService.getGroupKey(wrapper);
|
||||
const remainingInGroup = NotificationService.notifications.filter(n => NotificationService.getGroupKey(n) === groupKey);
|
||||
|
||||
if (remainingInGroup.length <= 1) {
|
||||
NotificationService.clearGroupExpansionState(groupKey);
|
||||
}
|
||||
|
||||
NotificationService.cleanupExpansionStates();
|
||||
NotificationService._recomputeGroupsLater();
|
||||
}
|
||||
|
||||
function onAboutToDestroy(): void {
|
||||
wrapper.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,150 +655,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
component NotifWrapper: QtObject {
|
||||
id: wrapper
|
||||
|
||||
property bool popup: false
|
||||
property bool removedByLimit: false
|
||||
property bool isPersistent: true
|
||||
property int seq: 0
|
||||
property string persistedImagePath: ""
|
||||
|
||||
onPopupChanged: {
|
||||
if (!popup) {
|
||||
removeFromVisibleNotifications(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
readonly property Timer timer: Timer {
|
||||
interval: {
|
||||
if (!wrapper.notification)
|
||||
return 5000;
|
||||
switch (wrapper.urgency) {
|
||||
case NotificationUrgency.Low:
|
||||
return SettingsData.notificationTimeoutLow;
|
||||
case NotificationUrgency.Critical:
|
||||
return SettingsData.notificationTimeoutCritical;
|
||||
default:
|
||||
return SettingsData.notificationTimeoutNormal;
|
||||
}
|
||||
}
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (interval > 0) {
|
||||
wrapper.popup = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly property date time: new Date()
|
||||
readonly property string timeStr: {
|
||||
root.timeUpdateTick;
|
||||
root.clockFormatChanged;
|
||||
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - time.getTime();
|
||||
const minutes = Math.floor(diff / 60000);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
|
||||
if (hours < 1) {
|
||||
if (minutes < 1) {
|
||||
return "now";
|
||||
}
|
||||
return `${minutes}m ago`;
|
||||
}
|
||||
|
||||
const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate());
|
||||
const daysDiff = Math.floor((nowDate - timeDate) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysDiff === 0) {
|
||||
return formatTime(time);
|
||||
}
|
||||
|
||||
try {
|
||||
const localeName = (typeof I18n !== "undefined" && I18n.locale) ? I18n.locale().name : "en-US";
|
||||
const weekday = time.toLocaleDateString(localeName, {
|
||||
weekday: "long"
|
||||
});
|
||||
return `${weekday}, ${formatTime(time)}`;
|
||||
} catch (e) {
|
||||
return formatTime(time);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(date) {
|
||||
let use24Hour = true;
|
||||
try {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.use24HourClock !== undefined) {
|
||||
use24Hour = SettingsData.use24HourClock;
|
||||
}
|
||||
} catch (e) {
|
||||
use24Hour = true;
|
||||
}
|
||||
|
||||
if (use24Hour) {
|
||||
return date.toLocaleTimeString(Qt.locale(), "HH:mm");
|
||||
} else {
|
||||
return date.toLocaleTimeString(Qt.locale(), "h:mm AP");
|
||||
}
|
||||
}
|
||||
|
||||
required property Notification notification
|
||||
readonly property string summary: (notification?.summary ?? "").replace(/<img\b[^>]*>/gi, "")
|
||||
readonly property string body: (notification?.body ?? "").replace(/<img\b[^>]*>/gi, "")
|
||||
readonly property string htmlBody: root._resolveHtmlBody(body)
|
||||
readonly property string appIcon: notification?.appIcon ?? ""
|
||||
readonly property string appName: {
|
||||
if (!notification)
|
||||
return "app";
|
||||
if (notification.appName == "") {
|
||||
const entry = DesktopEntries.heuristicLookup(notification.desktopEntry);
|
||||
if (entry && entry.name)
|
||||
return entry.name.toLowerCase();
|
||||
}
|
||||
return notification.appName || "app";
|
||||
}
|
||||
readonly property string desktopEntry: notification?.desktopEntry ?? ""
|
||||
readonly property string image: notification?.image ?? ""
|
||||
readonly property string cleanImage: {
|
||||
if (!image)
|
||||
return "";
|
||||
return Paths.strip(image);
|
||||
}
|
||||
property int urgencyOverride: notification?.urgency ?? NotificationUrgency.Normal
|
||||
readonly property int urgency: urgencyOverride
|
||||
readonly property list<NotificationAction> actions: notification?.actions ?? []
|
||||
|
||||
readonly property Connections conn: Connections {
|
||||
target: wrapper.notification?.Retainable ?? null
|
||||
|
||||
function onDropped(): void {
|
||||
root.allWrappers = root.allWrappers.filter(w => w !== wrapper);
|
||||
root.notifications = root.notifications.filter(w => w !== wrapper);
|
||||
|
||||
if (root.bulkDismissing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupKey = getGroupKey(wrapper);
|
||||
const remainingInGroup = root.notifications.filter(n => getGroupKey(n) === groupKey);
|
||||
|
||||
if (remainingInGroup.length <= 1) {
|
||||
clearGroupExpansionState(groupKey);
|
||||
}
|
||||
|
||||
cleanupExpansionStates();
|
||||
root._recomputeGroupsLater();
|
||||
}
|
||||
|
||||
function onAboutToDestroy(): void {
|
||||
wrapper.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notifComponent
|
||||
NotifWrapper {}
|
||||
|
||||
Reference in New Issue
Block a user