formatter
This commit is contained in:
+170
-139
@@ -12,68 +12,65 @@ import qs.Daemons
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
implicitWidth: layout.implicitWidth + 5 * 2
|
||||
implicitHeight: layout.implicitHeight + 5 * 2
|
||||
|
||||
readonly property int topMargin: 0
|
||||
readonly property int rounding: 6
|
||||
readonly property int topMargin: 0
|
||||
required property var wrapper
|
||||
|
||||
required property var wrapper
|
||||
implicitHeight: layout.implicitHeight + 5 * 2
|
||||
implicitWidth: layout.implicitWidth + 5 * 2
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
implicitWidth: stack.currentItem ? stack.currentItem.childrenRect.height : 0
|
||||
spacing: 12
|
||||
|
||||
RowLayout {
|
||||
id: tabBar
|
||||
spacing: 6
|
||||
Layout.fillWidth: true
|
||||
|
||||
property int tabHeight: 36
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
CustomClippingRect {
|
||||
radius: 6
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: tabBar.tabHeight
|
||||
|
||||
color: stack.currentIndex === 0 ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 6
|
||||
|
||||
StateLayer {
|
||||
|
||||
function onClicked(): void {
|
||||
stack.currentIndex = 0;
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Volumes")
|
||||
anchors.centerIn: parent
|
||||
color: stack.currentIndex === 0 ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3primary
|
||||
text: qsTr("Volumes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
radius: 6
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: tabBar.tabHeight
|
||||
|
||||
color: stack.currentIndex === 1 ? DynamicColors.palette.m3primary : DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 6
|
||||
|
||||
StateLayer {
|
||||
|
||||
function onClicked(): void {
|
||||
stack.currentIndex = 1;
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Devices")
|
||||
anchors.centerIn: parent
|
||||
color: stack.currentIndex === 1 ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3primary
|
||||
text: qsTr("Devices")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,175 +78,245 @@ Item {
|
||||
|
||||
StackLayout {
|
||||
id: stack
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: currentIndex === 0 ? vol.childrenRect.height : dev.childrenRect.height
|
||||
currentIndex: 0
|
||||
|
||||
VolumesTab { id: vol }
|
||||
DevicesTab { id: dev }
|
||||
|
||||
Behavior on currentIndex {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: stack
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "opacity"
|
||||
target: stack
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: stack
|
||||
property: "scale"
|
||||
to: 0.9
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "scale"
|
||||
target: stack
|
||||
to: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAction {}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: stack
|
||||
property: "opacity"
|
||||
to: 1
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "opacity"
|
||||
target: stack
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: stack
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "scale"
|
||||
target: stack
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VolumesTab {
|
||||
id: vol
|
||||
|
||||
}
|
||||
|
||||
DevicesTab {
|
||||
id: dev
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component DevicesTab: ColumnLayout {
|
||||
spacing: 12
|
||||
|
||||
ButtonGroup {
|
||||
id: sinks
|
||||
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: sources
|
||||
|
||||
}
|
||||
|
||||
CustomText {
|
||||
font.weight: 500
|
||||
text: qsTr("Output device")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Audio.sinks
|
||||
|
||||
CustomRadioButton {
|
||||
required property PwNode modelData
|
||||
|
||||
ButtonGroup.group: sinks
|
||||
checked: Audio.sink?.id === modelData.id
|
||||
text: modelData.description
|
||||
|
||||
onClicked: Audio.setAudioSink(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: 10
|
||||
font.weight: 500
|
||||
text: qsTr("Input device")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Audio.sources
|
||||
|
||||
CustomRadioButton {
|
||||
required property PwNode modelData
|
||||
|
||||
ButtonGroup.group: sources
|
||||
checked: Audio.source?.id === modelData.id
|
||||
text: modelData.description
|
||||
|
||||
onClicked: Audio.setAudioSource(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
component VolumesTab: ColumnLayout {
|
||||
spacing: 12
|
||||
|
||||
CustomRect {
|
||||
Layout.topMargin: root.topMargin
|
||||
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
|
||||
Layout.topMargin: root.topMargin
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
radius: root.rounding
|
||||
|
||||
RowLayout {
|
||||
id: outputVolume
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.spacing.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 15
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
text: "speaker"
|
||||
font.pointSize: 22
|
||||
text: "speaker"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
text: "Output Volume"
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
text: "Output Volume"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("%1").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`);
|
||||
font.bold: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
font.bold: true
|
||||
text: qsTr("%1").arg(Audio.muted ? qsTr("Muted") : `${Math.round(Audio.volume * 100)}%`)
|
||||
}
|
||||
}
|
||||
|
||||
CustomMouseArea {
|
||||
Layout.bottomMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 10
|
||||
Layout.bottomMargin: 5
|
||||
|
||||
CustomSlider {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 10
|
||||
value: Audio.volume
|
||||
onMoved: Audio.setVolume(value)
|
||||
|
||||
Behavior on value { Anim {} }
|
||||
Behavior on value {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onMoved: Audio.setVolume(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CustomRect {
|
||||
Layout.topMargin: root.topMargin
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
|
||||
Layout.topMargin: root.topMargin
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
radius: root.rounding
|
||||
|
||||
RowLayout {
|
||||
id: inputVolume
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.spacing.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 15
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
anchors.alignWhenCentered: false
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
text: "mic"
|
||||
font.pointSize: 22
|
||||
text: "mic"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
text: "Input Volume"
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
text: "Input Volume"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("%1").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`);
|
||||
font.bold: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
font.bold: true
|
||||
text: qsTr("%1").arg(Audio.sourceMuted ? qsTr("Muted") : `${Math.round(Audio.sourceVolume * 100)}%`)
|
||||
}
|
||||
}
|
||||
|
||||
CustomMouseArea {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 5
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 10
|
||||
|
||||
CustomSlider {
|
||||
@@ -257,9 +324,13 @@ Item {
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 10
|
||||
value: Audio.sourceVolume
|
||||
onMoved: Audio.setSourceVolume(value)
|
||||
|
||||
Behavior on value { Anim {} }
|
||||
Behavior on value {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onMoved: Audio.setSourceVolume(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,10 +338,9 @@ Item {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.topMargin: root.topMargin
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
|
||||
Layout.topMargin: root.topMargin
|
||||
color: DynamicColors.tPalette.m3outline
|
||||
}
|
||||
|
||||
@@ -280,39 +350,40 @@ Item {
|
||||
CustomRect {
|
||||
id: appBox
|
||||
|
||||
Layout.topMargin: root.topMargin
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 42 + Appearance.spacing.smaller * 2
|
||||
Layout.topMargin: root.topMargin
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
radius: root.rounding
|
||||
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
RowLayout {
|
||||
id: layoutVolume
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.spacing.smaller
|
||||
spacing: 15
|
||||
|
||||
|
||||
CustomRect {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "volume_up"
|
||||
font.pointSize: 22
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: 22
|
||||
text: "volume_up"
|
||||
|
||||
StateLayer {
|
||||
radius: 1000
|
||||
|
||||
onClicked: {
|
||||
appBox.modelData.audio.muted = !appBox.modelData.audio.muted;
|
||||
}
|
||||
@@ -326,42 +397,46 @@ Item {
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
text: Audio.getStreamName(appBox.modelData)
|
||||
|
||||
elide: Text.ElideRight
|
||||
elideWidth: root.width - 50
|
||||
text: Audio.getStreamName(appBox.modelData)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
text: metrics.elidedText
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: metrics.elidedText
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`);
|
||||
font.bold: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.fillHeight: true
|
||||
font.bold: true
|
||||
text: qsTr("%1").arg(appBox.modelData.audio.muted ? qsTr("Muted") : `${Math.round(appBox.modelData.audio.volume * 100)}%`)
|
||||
}
|
||||
}
|
||||
|
||||
CustomMouseArea {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 10
|
||||
|
||||
CustomSlider {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: 10
|
||||
value: appBox.modelData.audio.volume
|
||||
|
||||
onMoved: {
|
||||
Audio.setStreamVolume(appBox.modelData, value)
|
||||
Audio.setStreamVolume(appBox.modelData, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,48 +445,4 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component DevicesTab: ColumnLayout {
|
||||
spacing: 12
|
||||
|
||||
ButtonGroup { id: sinks }
|
||||
ButtonGroup { id: sources }
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Output device")
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Audio.sinks
|
||||
|
||||
CustomRadioButton {
|
||||
required property PwNode modelData
|
||||
|
||||
ButtonGroup.group: sinks
|
||||
checked: Audio.sink?.id === modelData.id
|
||||
onClicked: Audio.setAudioSink(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: 10
|
||||
text: qsTr("Input device")
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: Audio.sources
|
||||
|
||||
CustomRadioButton {
|
||||
required property PwNode modelData
|
||||
|
||||
ButtonGroup.group: sources
|
||||
checked: Audio.source?.id === modelData.id
|
||||
onClicked: Audio.setAudioSource(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+96
-91
@@ -8,113 +8,118 @@ import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: expanded ? 300 : 150
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
id: root
|
||||
|
||||
property bool expanded: false
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
property color barColor: DynamicColors.palette.m3primary
|
||||
property color barColor: DynamicColors.palette.m3primary
|
||||
property bool expanded: false
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: expanded ? 300 : 150
|
||||
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 22
|
||||
radius: height / 2
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
height: 22
|
||||
radius: height / 2
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: "#30ffffff"
|
||||
border.width: 0
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 10
|
||||
rightMargin: 15
|
||||
}
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
border.color: "#30ffffff"
|
||||
border.width: 0
|
||||
color: "transparent"
|
||||
height: parent.height
|
||||
radius: width / 2
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: Audio.muted ? "volume_off" : "volume_up"
|
||||
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
|
||||
}
|
||||
RowLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 10
|
||||
rightMargin: 15
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Audio.muted ? DynamicColors.palette.m3error : root.textColor
|
||||
font.pixelSize: 18
|
||||
text: Audio.muted ? "volume_off" : "volume_up"
|
||||
}
|
||||
|
||||
implicitHeight: 4
|
||||
radius: 20
|
||||
color: "#50ffffff"
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
color: "#50ffffff"
|
||||
implicitHeight: 4
|
||||
radius: 20
|
||||
|
||||
Rectangle {
|
||||
id: sinkVolumeBar
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
Rectangle {
|
||||
id: sinkVolumeBar
|
||||
|
||||
implicitWidth: parent.width * ( Audio.volume ?? 0 )
|
||||
radius: parent.radius
|
||||
color: Audio.muted ? DynamicColors.palette.m3error : root.barColor
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
implicitWidth: parent.width * (Audio.volume ?? 0)
|
||||
radius: parent.radius
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: Audio.sourceMuted ? "mic_off" : "mic"
|
||||
color: ( Audio.sourceMuted ?? false ) ? DynamicColors.palette.m3error : root.textColor
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: 4
|
||||
radius: 20
|
||||
color: "#50ffffff"
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor
|
||||
font.pixelSize: 18
|
||||
text: Audio.sourceMuted ? "mic_off" : "mic"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: sourceVolumeBar
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
color: "#50ffffff"
|
||||
implicitHeight: 4
|
||||
radius: 20
|
||||
|
||||
implicitWidth: parent.width * ( Audio.sourceVolume ?? 0 )
|
||||
radius: parent.radius
|
||||
color: ( Audio.sourceMuted ?? false ) ? DynamicColors.palette.m3error : root.barColor
|
||||
Rectangle {
|
||||
id: sourceVolumeBar
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.barColor
|
||||
implicitWidth: parent.width * (Audio.sourceVolume ?? 0)
|
||||
radius: parent.radius
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+51
-50
@@ -4,64 +4,65 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property bool invertBottomRounding
|
||||
readonly property real rounding: 8
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
property real ibr: invertBottomRounding ? -1 : 1
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
property real ibr: invertBottomRounding ? -1 : 1
|
||||
required property bool invertBottomRounding
|
||||
readonly property real rounding: 8
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY - root.roundingY * root.ibr
|
||||
}
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY * root.ibr
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: root.invertBottomRounding ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY - root.roundingY * root.ibr
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
direction: root.invertBottomRounding ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY * root.ibr
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -( root.wrapper.height - root.roundingY * 2 )
|
||||
}
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
}
|
||||
|
||||
+60
-35
@@ -13,24 +13,23 @@ import qs.Modules.Network
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property int vPadding: 6
|
||||
required property Wrapper popouts
|
||||
required property PersistentProperties visibilities
|
||||
required property PanelWindow bar
|
||||
required property Wrapper popouts
|
||||
required property ShellScreen screen
|
||||
readonly property int vPadding: 6
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
function checkPopout(x: real): void {
|
||||
const ch = childAt(x, 2) as WrappedLoader;
|
||||
|
||||
if (!ch) {
|
||||
if ( !popouts.currentName.includes("traymenu") )
|
||||
if (!popouts.currentName.includes("traymenu"))
|
||||
popouts.hasCurrent = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( visibilities.sidebar || visibilities.dashboard )
|
||||
if (visibilities.sidebar || visibilities.dashboard)
|
||||
return;
|
||||
|
||||
const id = ch.id;
|
||||
@@ -38,49 +37,50 @@ RowLayout {
|
||||
const item = ch.item;
|
||||
const itemWidth = item.implicitWidth;
|
||||
|
||||
|
||||
if (id === "audio" && Config.barConfig.popouts.audio) {
|
||||
popouts.currentName = "audio";
|
||||
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;
|
||||
} else if ( id === "resources" && Config.barConfig.popouts.resources ) {
|
||||
} else if (id === "resources" && Config.barConfig.popouts.resources) {
|
||||
popouts.currentName = "resources";
|
||||
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;
|
||||
} else if ( id === "tray" && Config.barConfig.popouts.tray ) {
|
||||
const index = Math.floor((( x - top ) / item.implicitWidth ) * item.items.count );
|
||||
const trayItem = item.items.itemAt( index );
|
||||
if ( trayItem ) {
|
||||
} else if (id === "tray" && Config.barConfig.popouts.tray) {
|
||||
const index = Math.floor(((x - top) / item.implicitWidth) * item.items.count);
|
||||
const trayItem = item.items.itemAt(index);
|
||||
if (trayItem) {
|
||||
// popouts.currentName = `traymenu${ index }`;
|
||||
// popouts.currentCenter = Qt.binding( () => trayItem.mapToItem( root, trayItem.implicitWidth / 2, 0 ).x );
|
||||
// popouts.hasCurrent = true;
|
||||
} else {
|
||||
// popouts.hasCurrent = false;
|
||||
}
|
||||
} else if ( id === "clock" && Config.barConfig.popouts.clock ) {
|
||||
} else if (id === "clock" && Config.barConfig.popouts.clock) {
|
||||
// Calendar.displayYear = new Date().getFullYear();
|
||||
// Calendar.displayMonth = new Date().getMonth();
|
||||
// popouts.currentName = "calendar";
|
||||
// popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x );
|
||||
// popouts.hasCurrent = true;
|
||||
} else if ( id === "network" && Config.barConfig.popouts.network ) {
|
||||
} else if (id === "network" && Config.barConfig.popouts.network) {
|
||||
popouts.currentName = "network";
|
||||
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;
|
||||
} else if ( id === "upower" && Config.barConfig.popouts.upower ) {
|
||||
} else if (id === "upower" && Config.barConfig.popouts.upower) {
|
||||
popouts.currentName = "upower";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-overview"
|
||||
|
||||
onPressed: {
|
||||
Hyprland.refreshWorkspaces();
|
||||
Hyprland.refreshMonitors();
|
||||
if ( root.popouts.hasCurrent && root.popouts.currentName === "overview" ) {
|
||||
if (root.popouts.hasCurrent && root.popouts.currentName === "overview") {
|
||||
root.popouts.hasCurrent = false;
|
||||
} else {
|
||||
root.popouts.currentName = "overview";
|
||||
@@ -92,6 +92,7 @@ RowLayout {
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: Config.barConfig.entries
|
||||
|
||||
DelegateChooser {
|
||||
@@ -99,67 +100,87 @@ RowLayout {
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "spacer"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "workspaces"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Workspaces {
|
||||
bar: root.bar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "audio"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: AudioWidget {}
|
||||
sourceComponent: AudioWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "tray"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: TrayWidget {
|
||||
bar: root.bar
|
||||
popouts: root.popouts
|
||||
loader: root
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "resources"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Resources {}
|
||||
sourceComponent: Resources {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "updates"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: UpdatesWidget {}
|
||||
sourceComponent: UpdatesWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "notifBell"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: NotifBell {
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "clock"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: Clock {
|
||||
loader: root
|
||||
popouts: root.popouts
|
||||
visibilities: root.visibilities
|
||||
loader: root
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "activeWindow"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: WindowTitle {
|
||||
bar: root
|
||||
@@ -167,16 +188,22 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "upower"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: UPowerWidget {}
|
||||
sourceComponent: UPowerWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "network"
|
||||
|
||||
delegate: WrappedLoader {
|
||||
sourceComponent: NetworkWidget {}
|
||||
sourceComponent: NetworkWidget {
|
||||
}
|
||||
}
|
||||
}
|
||||
// DelegateChoice {
|
||||
@@ -195,15 +222,12 @@ RowLayout {
|
||||
required property string id
|
||||
required property int index
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
|
||||
function findFirstEnabled(): Item {
|
||||
const count = repeater.count;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (item?.enabled)
|
||||
return item;
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -212,15 +236,16 @@ RowLayout {
|
||||
for (let i = repeater.count - 1; i >= 0; i--) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (item?.enabled)
|
||||
return item;
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: findFirstEnabled() === this ? root.vPadding : 0
|
||||
Layout.rightMargin: findLastEnabled() === this ? root.vPadding : 0
|
||||
|
||||
visible: enabled
|
||||
active: enabled
|
||||
visible: enabled
|
||||
}
|
||||
}
|
||||
|
||||
+30
-28
@@ -8,42 +8,44 @@ import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Item bar
|
||||
required property Item bar
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.fill: parent
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: Config.barConfig.autoHide && !root.visibilities.bar ? "transparent" : DynamicColors.palette.m3surface
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: Config.barConfig.autoHide && !root.visibilities.bar ? "transparent" : DynamicColors.palette.m3surface
|
||||
layer.enabled: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskInverted: true
|
||||
maskSource: mask
|
||||
maskSpreadAtMin: 1
|
||||
maskThresholdMin: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
maskSource: mask
|
||||
maskEnabled: true
|
||||
maskInverted: true
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
}
|
||||
Item {
|
||||
id: mask
|
||||
|
||||
Item {
|
||||
id: mask
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight
|
||||
topLeftRadius: 8
|
||||
topRightRadius: 8
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Config.barConfig.autoHide && !root.visibilities.bar ? 4 : root.bar.implicitHeight
|
||||
topLeftRadius: 8
|
||||
topRightRadius: 8
|
||||
Behavior on anchors.topMargin {
|
||||
Anim {}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,17 @@ RowLayout {
|
||||
spacing: 12
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
color: "transparent"
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "arrow_back_2"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
fill: 1
|
||||
font.pointSize: 24
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
text: "arrow_back_2"
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
@@ -36,30 +36,27 @@ RowLayout {
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: new Date(Calendar.displayYear, Calendar.displayMonth, 1).toLocaleDateString(
|
||||
Qt.locale(),
|
||||
"MMMM yyyy"
|
||||
)
|
||||
font.weight: 600
|
||||
font.pointSize: 14
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 14
|
||||
font.weight: 600
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: new Date(Calendar.displayYear, Calendar.displayMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy")
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 40
|
||||
color: "transparent"
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "arrow_back_2"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
fill: 1
|
||||
font.pointSize: 24
|
||||
rotation: 180
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
text: "arrow_back_2"
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
|
||||
@@ -11,8 +11,8 @@ Item {
|
||||
|
||||
required property Item wrapper
|
||||
|
||||
implicitWidth: layout.childrenRect.width + layout.anchors.margins * 2
|
||||
implicitHeight: layout.childrenRect.height + layout.anchors.margins * 2
|
||||
implicitWidth: layout.childrenRect.width + layout.anchors.margins * 2
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
@@ -59,17 +59,17 @@ Item {
|
||||
|
||||
DayOfWeekRow {
|
||||
id: dayOfWeekRow
|
||||
locale: Qt.locale()
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
locale: Qt.locale()
|
||||
}
|
||||
|
||||
MonthGrid {
|
||||
locale: Qt.locale()
|
||||
|
||||
wrapper: root.wrapper
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
Layout.preferredWidth: childrenRect.width
|
||||
locale: Qt.locale()
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,39 +7,36 @@ import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var locale
|
||||
required property var locale
|
||||
|
||||
spacing: 4
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: 7
|
||||
Repeater {
|
||||
model: 7
|
||||
|
||||
Item {
|
||||
required property int index
|
||||
Item {
|
||||
readonly property string dayName: {
|
||||
// Get the day name for this column
|
||||
const dayIndex = (index + Calendar.weekStartDay) % 7;
|
||||
return root.locale.dayName(dayIndex, Locale.ShortFormat);
|
||||
}
|
||||
required property int index
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 30
|
||||
|
||||
readonly property string dayName: {
|
||||
// Get the day name for this column
|
||||
const dayIndex = (index + Calendar.weekStartDay) % 7;
|
||||
return root.locale.dayName(dayIndex, Locale.ShortFormat);
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: parent.dayName
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
opacity: 0.8
|
||||
font.weight: 500
|
||||
font.pointSize: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 11
|
||||
font.weight: 500
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 0.8
|
||||
text: parent.dayName
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,48 +13,49 @@ GridLayout {
|
||||
required property var locale
|
||||
required property Item wrapper
|
||||
|
||||
columnSpacing: 4
|
||||
columns: 7
|
||||
rowSpacing: 4
|
||||
columnSpacing: 4
|
||||
uniformCellWidths: true
|
||||
uniformCellHeights: true
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
target: root
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
uniformCellWidths: true
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: ScriptModel {
|
||||
values: Calendar.getWeeksForMonth(Calendar.displayMonth, Calendar.displayYear)
|
||||
|
||||
Behavior on values {
|
||||
SequentialAnimation {
|
||||
id: switchAnim
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
from: 1.0
|
||||
property: "opacity"
|
||||
to: 0.0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "scale"
|
||||
from: 1.0
|
||||
property: "scale"
|
||||
to: 0.8
|
||||
}
|
||||
}
|
||||
PropertyAction {}
|
||||
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
from: 0.0
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "scale"
|
||||
from: 0.8
|
||||
property: "scale"
|
||||
to: 1.0
|
||||
}
|
||||
}
|
||||
@@ -63,15 +64,11 @@ GridLayout {
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: width
|
||||
|
||||
|
||||
radius: 1000
|
||||
Layout.preferredWidth: 40
|
||||
color: {
|
||||
if (modelData.isToday) {
|
||||
console.log(width);
|
||||
@@ -79,39 +76,52 @@ GridLayout {
|
||||
}
|
||||
return "transparent";
|
||||
}
|
||||
radius: 1000
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200 }
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: parent.modelData.day.toString()
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
opacity: parent.modelData.isCurrentMonth ? 1.0 : 0.4
|
||||
color: {
|
||||
if (parent.modelData.isToday) {
|
||||
return DynamicColors.palette.m3onPrimaryContainer;
|
||||
}
|
||||
return DynamicColors.palette.m3onSurface;
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: parent.modelData.isCurrentMonth ? 1.0 : 0.4
|
||||
text: parent.modelData.day.toString()
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200 }
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200 }
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
|
||||
onClicked: {
|
||||
console.log(`Selected date: ${parent.modelData.day}/${parent.modelData.month + 1}/${parent.modelData.year}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
target: root
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,38 +8,38 @@ import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
spacing: 4
|
||||
readonly property var weekNumbers: Calendar.getWeekNumbers(Calendar.displayMonth, Calendar.displayYear)
|
||||
|
||||
readonly property var weekNumbers: Calendar.getWeekNumbers(Calendar.displayMonth, Calendar.displayYear)
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.weekNumbers
|
||||
}
|
||||
|
||||
Item {
|
||||
id: weekItem
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 20
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Item {
|
||||
id: weekItem
|
||||
|
||||
required property int index
|
||||
required property var modelData
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
CustomText {
|
||||
id: weekText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 20
|
||||
|
||||
anchors.centerIn: parent
|
||||
CustomText {
|
||||
id: weekText
|
||||
|
||||
text: weekItem.modelData
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
opacity: 0.5
|
||||
font.pointSize: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 0.5
|
||||
text: weekItem.modelData
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-11
@@ -8,35 +8,38 @@ import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property PersistentProperties visibilities
|
||||
required property Wrapper popouts
|
||||
required property RowLayout loader
|
||||
|
||||
implicitWidth: timeText.contentWidth + 5 * 2
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
required property RowLayout loader
|
||||
required property Wrapper popouts
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: timeText.contentWidth + 5 * 2
|
||||
|
||||
CustomRect {
|
||||
anchors.bottomMargin: 3
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 3
|
||||
anchors.bottomMargin: 3
|
||||
radius: 4
|
||||
color: "transparent"
|
||||
radius: 4
|
||||
|
||||
CustomText {
|
||||
id: timeText
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: Time.dateStr
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
text: Time.dateStr
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
||||
onClicked: {
|
||||
root.visibilities.dashboard = !root.visibilities.dashboard;
|
||||
}
|
||||
|
||||
+103
-100
@@ -11,72 +11,75 @@ import qs.Modules.Network
|
||||
import qs.Modules.UPower
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Item wrapper
|
||||
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
|
||||
readonly property Item current: currentPopout?.item ?? null
|
||||
readonly property Item current: currentPopout?.item ?? null
|
||||
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
|
||||
required property Item wrapper
|
||||
|
||||
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
|
||||
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
|
||||
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
|
||||
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
|
||||
|
||||
Item {
|
||||
id: content
|
||||
Item {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.fill: parent
|
||||
|
||||
Popout {
|
||||
name: "audio"
|
||||
sourceComponent: AudioPopup {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
Popout {
|
||||
name: "audio"
|
||||
|
||||
Popout {
|
||||
name: "resources"
|
||||
sourceComponent: ResourcePopout {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
sourceComponent: AudioPopup {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: [ ...SystemTray.items.values ]
|
||||
}
|
||||
Popout {
|
||||
name: "resources"
|
||||
|
||||
Popout {
|
||||
id: trayMenu
|
||||
sourceComponent: ResourcePopout {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
|
||||
required property SystemTrayItem modelData
|
||||
required property int index
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: [...SystemTray.items.values]
|
||||
}
|
||||
|
||||
name: `traymenu${index}`
|
||||
sourceComponent: trayMenuComponent
|
||||
Popout {
|
||||
id: trayMenu
|
||||
|
||||
Connections {
|
||||
target: root.wrapper
|
||||
required property int index
|
||||
required property SystemTrayItem modelData
|
||||
|
||||
function onHasCurrentChanged(): void {
|
||||
if ( root.wrapper.hasCurrent && trayMenu.shouldBeActive ) {
|
||||
trayMenu.sourceComponent = null;
|
||||
trayMenu.sourceComponent = trayMenuComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
name: `traymenu${index}`
|
||||
sourceComponent: trayMenuComponent
|
||||
|
||||
Component {
|
||||
id: trayMenuComponent
|
||||
Connections {
|
||||
function onHasCurrentChanged(): void {
|
||||
if (root.wrapper.hasCurrent && trayMenu.shouldBeActive) {
|
||||
trayMenu.sourceComponent = null;
|
||||
trayMenu.sourceComponent = trayMenuComponent;
|
||||
}
|
||||
}
|
||||
|
||||
TrayMenuPopout {
|
||||
popouts: root.wrapper
|
||||
trayItem: trayMenu.modelData.menu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
target: root.wrapper
|
||||
}
|
||||
|
||||
Component {
|
||||
id: trayMenuComponent
|
||||
|
||||
TrayMenuPopout {
|
||||
popouts: root.wrapper
|
||||
trayItem: trayMenu.modelData.menu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popout {
|
||||
name: "calendar"
|
||||
|
||||
sourceComponent: CalendarPopup {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
@@ -86,8 +89,8 @@ Item {
|
||||
name: "overview"
|
||||
|
||||
sourceComponent: OverviewPopout {
|
||||
wrapper: root.wrapper
|
||||
screen: root.wrapper.screen
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,63 +109,63 @@ Item {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Popout: Loader {
|
||||
id: popout
|
||||
component Popout: Loader {
|
||||
id: popout
|
||||
|
||||
required property string name
|
||||
readonly property bool shouldBeActive: root.wrapper.currentName === name
|
||||
required property string name
|
||||
readonly property bool shouldBeActive: root.wrapper.currentName === name
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: false
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 5
|
||||
opacity: 0
|
||||
scale: 0.8
|
||||
|
||||
opacity: 0
|
||||
scale: 0.8
|
||||
active: false
|
||||
states: State {
|
||||
name: "active"
|
||||
when: popout.shouldBeActive
|
||||
|
||||
states: State {
|
||||
name: "active"
|
||||
when: popout.shouldBeActive
|
||||
PropertyChanges {
|
||||
popout.active: true
|
||||
popout.opacity: 1
|
||||
popout.scale: 1
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "active"
|
||||
to: ""
|
||||
|
||||
PropertyChanges {
|
||||
popout.active: true
|
||||
popout.opacity: 1
|
||||
popout.scale: 1
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: "active"
|
||||
to: ""
|
||||
PropertyAction {
|
||||
property: "active"
|
||||
target: popout
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: ""
|
||||
to: "active"
|
||||
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
PropertyAction {
|
||||
target: popout
|
||||
property: "active"
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: ""
|
||||
to: "active"
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
property: "active"
|
||||
target: popout
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
target: popout
|
||||
property: "active"
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ CustomRect {
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 6
|
||||
anchors.bottomMargin: 6
|
||||
implicitWidth: 40
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 6
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitWidth: 40
|
||||
radius: 1000
|
||||
|
||||
StateLayer {
|
||||
@@ -25,7 +25,7 @@ CustomRect {
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "widgets"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
text: "widgets"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,62 +4,63 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: 8
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: 8
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,81 +8,79 @@ import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
|
||||
required property PersistentProperties state
|
||||
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
|
||||
ClippingRectangle {
|
||||
id: viewWrapper
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
ClippingRectangle {
|
||||
id: viewWrapper
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: "transparent"
|
||||
radius: 6
|
||||
|
||||
radius: 6
|
||||
color: "transparent"
|
||||
Item {
|
||||
id: view
|
||||
|
||||
Item {
|
||||
id: view
|
||||
readonly property int currentIndex: root.state.currentTab
|
||||
readonly property Item currentItem: row.children[currentIndex]
|
||||
|
||||
readonly property int currentIndex: root.state.currentTab
|
||||
readonly property Item currentItem: row.children[currentIndex]
|
||||
anchors.fill: parent
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
|
||||
anchors.fill: parent
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
Pane {
|
||||
index: 0
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
Pane {
|
||||
index: 0
|
||||
sourceComponent: Dash {
|
||||
state: root.state
|
||||
sourceComponent: Dash {
|
||||
state: root.state
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
component Pane: Loader {
|
||||
id: pane
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
required property int index
|
||||
|
||||
component Pane: Loader {
|
||||
id: pane
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
required property int index
|
||||
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => {
|
||||
// Always keep current tab loaded
|
||||
if (pane.index === view.currentIndex)
|
||||
return true;
|
||||
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
|
||||
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
|
||||
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
|
||||
})
|
||||
}
|
||||
Component.onCompleted: active = Qt.binding(() => {
|
||||
// Always keep current tab loaded
|
||||
if (pane.index === view.currentIndex)
|
||||
return true;
|
||||
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
|
||||
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
|
||||
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+86
-86
@@ -9,18 +9,16 @@ import qs.Config
|
||||
import qs.Modules.Dashboard.Dash
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property PersistentProperties state
|
||||
readonly property bool dashboardVisible: visibilities.dashboard
|
||||
|
||||
property int radius: 6
|
||||
required property PersistentProperties state
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
rowSpacing: Appearance.spacing.smaller
|
||||
columnSpacing: Appearance.spacing.smaller
|
||||
|
||||
columnSpacing: Appearance.spacing.smaller
|
||||
opacity: 0
|
||||
rowSpacing: Appearance.spacing.smaller
|
||||
scale: 0.9
|
||||
|
||||
onDashboardVisibleChanged: {
|
||||
@@ -33,115 +31,117 @@ GridLayout {
|
||||
|
||||
ParallelAnimation {
|
||||
id: openAnim
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: closeAnim
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.column: 2
|
||||
Layout.columnSpan: 3
|
||||
Layout.preferredWidth: user.implicitWidth
|
||||
Layout.preferredHeight: user.implicitHeight
|
||||
Rect {
|
||||
Layout.column: 2
|
||||
Layout.columnSpan: 3
|
||||
Layout.preferredHeight: user.implicitHeight
|
||||
Layout.preferredWidth: user.implicitWidth
|
||||
radius: root.radius
|
||||
|
||||
radius: root.radius
|
||||
User {
|
||||
id: user
|
||||
|
||||
User {
|
||||
id: user
|
||||
state: root.state
|
||||
}
|
||||
}
|
||||
|
||||
state: root.state
|
||||
}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.row: 0
|
||||
Layout.columnSpan: 2
|
||||
Layout.preferredWidth: Config.dashboard.sizes.weatherWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: root.radius
|
||||
|
||||
Weather {}
|
||||
}
|
||||
|
||||
// Rect {
|
||||
// Layout.row: 1
|
||||
// Layout.preferredWidth: dateTime.implicitWidth
|
||||
// Layout.fillHeight: true
|
||||
//
|
||||
// radius: root.radius
|
||||
//
|
||||
// DateTime {
|
||||
// id: dateTime
|
||||
// }
|
||||
// }
|
||||
|
||||
Rect {
|
||||
Layout.row: 1
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: calendar.implicitHeight
|
||||
|
||||
radius: root.radius
|
||||
|
||||
Calendar {
|
||||
id: calendar
|
||||
|
||||
state: root.state
|
||||
}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.row: 1
|
||||
Layout.column: 3
|
||||
Rect {
|
||||
Layout.columnSpan: 2
|
||||
Layout.preferredWidth: resources.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: Config.dashboard.sizes.weatherWidth
|
||||
Layout.row: 0
|
||||
radius: root.radius
|
||||
|
||||
radius: root.radius
|
||||
Weather {
|
||||
}
|
||||
}
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
}
|
||||
}
|
||||
// Rect {
|
||||
// Layout.row: 1
|
||||
// Layout.preferredWidth: dateTime.implicitWidth
|
||||
// Layout.fillHeight: true
|
||||
//
|
||||
// radius: root.radius
|
||||
//
|
||||
// DateTime {
|
||||
// id: dateTime
|
||||
// }
|
||||
// }
|
||||
|
||||
Rect {
|
||||
Layout.row: 0
|
||||
Layout.column: 5
|
||||
Layout.rowSpan: 2
|
||||
Layout.preferredWidth: media.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
Rect {
|
||||
Layout.column: 0
|
||||
Layout.columnSpan: 3
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: calendar.implicitHeight
|
||||
Layout.row: 1
|
||||
radius: root.radius
|
||||
|
||||
radius: root.radius
|
||||
Calendar {
|
||||
id: calendar
|
||||
|
||||
Media {
|
||||
id: media
|
||||
}
|
||||
}
|
||||
state: root.state
|
||||
}
|
||||
}
|
||||
|
||||
component Rect: CustomRect {
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
}
|
||||
Rect {
|
||||
Layout.column: 3
|
||||
Layout.columnSpan: 2
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: resources.implicitWidth
|
||||
Layout.row: 1
|
||||
radius: root.radius
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Rect {
|
||||
Layout.column: 5
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: media.implicitWidth
|
||||
Layout.row: 0
|
||||
Layout.rowSpan: 2
|
||||
radius: root.radius
|
||||
|
||||
Media {
|
||||
id: media
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
component Rect: CustomRect {
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
}
|
||||
}
|
||||
|
||||
+184
-196
@@ -9,244 +9,232 @@ import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
CustomMouseArea {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var state
|
||||
readonly property int currMonth: state.currentDate.getMonth()
|
||||
readonly property int currYear: state.currentDate.getFullYear()
|
||||
required property var state
|
||||
|
||||
readonly property int currMonth: state.currentDate.getMonth()
|
||||
readonly property int currYear: state.currentDate.getFullYear()
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (event.angleDelta.y > 0)
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1);
|
||||
else if (event.angleDelta.y < 0)
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1);
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: inner.implicitHeight + inner.anchors.margins * 2
|
||||
acceptedButtons: Qt.MiddleButton
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: inner.implicitHeight + inner.anchors.margins * 2
|
||||
|
||||
acceptedButtons: Qt.MiddleButton
|
||||
onClicked: root.state.currentDate = new Date()
|
||||
onClicked: root.state.currentDate = new Date()
|
||||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (event.angleDelta.y > 0)
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1);
|
||||
else if (event.angleDelta.y < 0)
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1);
|
||||
}
|
||||
ColumnLayout {
|
||||
id: inner
|
||||
|
||||
ColumnLayout {
|
||||
id: inner
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
RowLayout {
|
||||
id: monthNavigationRow
|
||||
|
||||
RowLayout {
|
||||
id: monthNavigationRow
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
Item {
|
||||
implicitHeight: prevMonthText.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
Item {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: prevMonthText.implicitHeight + Appearance.padding.small * 2
|
||||
StateLayer {
|
||||
id: prevMonthStateLayer
|
||||
|
||||
StateLayer {
|
||||
id: prevMonthStateLayer
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1);
|
||||
}
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth - 1, 1);
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
id: prevMonthText
|
||||
|
||||
MaterialIcon {
|
||||
id: prevMonthText
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 700
|
||||
text: "chevron_left"
|
||||
}
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "chevron_left"
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 700
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: monthYearDisplay.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: monthYearDisplay.implicitWidth + Appearance.padding.small * 2
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date();
|
||||
}
|
||||
|
||||
implicitWidth: monthYearDisplay.implicitWidth + Appearance.padding.small * 2
|
||||
implicitHeight: monthYearDisplay.implicitHeight + Appearance.padding.small * 2
|
||||
anchors.fill: monthYearDisplay
|
||||
anchors.leftMargin: -Appearance.padding.normal
|
||||
anchors.margins: -Appearance.padding.small
|
||||
anchors.rightMargin: -Appearance.padding.normal
|
||||
disabled: {
|
||||
const now = new Date();
|
||||
return root.currMonth === now.getMonth() && root.currYear === now.getFullYear();
|
||||
}
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
anchors.fill: monthYearDisplay
|
||||
anchors.margins: -Appearance.padding.small
|
||||
anchors.leftMargin: -Appearance.padding.normal
|
||||
anchors.rightMargin: -Appearance.padding.normal
|
||||
CustomText {
|
||||
id: monthYearDisplay
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
disabled: {
|
||||
const now = new Date();
|
||||
return root.currMonth === now.getMonth() && root.currYear === now.getFullYear();
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.capitalization: Font.Capitalize
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
text: grid.title
|
||||
}
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
implicitHeight: nextMonthText.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
CustomText {
|
||||
id: monthYearDisplay
|
||||
StateLayer {
|
||||
id: nextMonthStateLayer
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: grid.title
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
font.capitalization: Font.Capitalize
|
||||
}
|
||||
}
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1);
|
||||
}
|
||||
|
||||
Item {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: nextMonthText.implicitHeight + Appearance.padding.small * 2
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: nextMonthStateLayer
|
||||
MaterialIcon {
|
||||
id: nextMonthText
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 700
|
||||
text: "chevron_right"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.state.currentDate = new Date(root.currYear, root.currMonth + 1, 1);
|
||||
}
|
||||
}
|
||||
DayOfWeekRow {
|
||||
id: daysRow
|
||||
|
||||
MaterialIcon {
|
||||
id: nextMonthText
|
||||
Layout.fillWidth: true
|
||||
locale: grid.locale
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "chevron_right"
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 700
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: CustomText {
|
||||
required property var model
|
||||
|
||||
DayOfWeekRow {
|
||||
id: daysRow
|
||||
color: (model.day === 0) ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurfaceVariant
|
||||
font.weight: 500
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: model.shortName
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
locale: grid.locale
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: grid.implicitHeight
|
||||
|
||||
delegate: CustomText {
|
||||
required property var model
|
||||
MonthGrid {
|
||||
id: grid
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: model.shortName
|
||||
font.weight: 500
|
||||
color: (model.day === 0) ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
anchors.fill: parent
|
||||
locale: Qt.locale("en_SE")
|
||||
month: root.currMonth
|
||||
spacing: 3
|
||||
year: root.currYear
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: grid.implicitHeight
|
||||
delegate: Item {
|
||||
id: dayItem
|
||||
|
||||
MonthGrid {
|
||||
id: grid
|
||||
required property var model
|
||||
|
||||
month: root.currMonth
|
||||
year: root.currYear
|
||||
implicitHeight: text.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
anchors.fill: parent
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
spacing: 3
|
||||
locale: Qt.locale("en_SE")
|
||||
anchors.centerIn: parent
|
||||
color: {
|
||||
const dayOfWeek = dayItem.model.date.getUTCDay();
|
||||
if (dayOfWeek === 6)
|
||||
return DynamicColors.palette.m3secondary;
|
||||
|
||||
delegate: Item {
|
||||
id: dayItem
|
||||
return DynamicColors.palette.m3onSurfaceVariant;
|
||||
}
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: dayItem.model.today || dayItem.model.month === grid.month ? 1 : 0.4
|
||||
text: grid.locale.toString(dayItem.model.day)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required property var model
|
||||
CustomRect {
|
||||
id: todayIndicator
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: text.implicitHeight + Appearance.padding.small * 2
|
||||
property Item today
|
||||
readonly property Item todayItem: grid.contentItem.children.find(c => c.model.today) ?? null
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
clip: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitHeight: today?.implicitHeight ?? 0
|
||||
implicitWidth: today?.implicitWidth ?? 0
|
||||
opacity: todayItem ? 1 : 0
|
||||
radius: Appearance.rounding.full
|
||||
scale: todayItem ? 1 : 0.7
|
||||
x: today ? today.x + (today.width - implicitWidth) / 2 : 0
|
||||
y: today?.y ?? 0
|
||||
|
||||
anchors.centerIn: parent
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: grid.locale.toString(dayItem.model.day)
|
||||
color: {
|
||||
const dayOfWeek = dayItem.model.date.getUTCDay();
|
||||
if (dayOfWeek === 6)
|
||||
return DynamicColors.palette.m3secondary;
|
||||
onTodayItemChanged: {
|
||||
if (todayItem)
|
||||
today = todayItem;
|
||||
}
|
||||
|
||||
return DynamicColors.palette.m3onSurfaceVariant;
|
||||
}
|
||||
opacity: dayItem.model.today || dayItem.model.month === grid.month ? 1 : 0.4
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: todayIndicator
|
||||
|
||||
readonly property Item todayItem: grid.contentItem.children.find(c => c.model.today) ?? null
|
||||
property Item today
|
||||
|
||||
onTodayItemChanged: {
|
||||
if (todayItem)
|
||||
today = todayItem;
|
||||
}
|
||||
|
||||
x: today ? today.x + (today.width - implicitWidth) / 2 : 0
|
||||
y: today?.y ?? 0
|
||||
|
||||
implicitWidth: today?.implicitWidth ?? 0
|
||||
implicitHeight: today?.implicitHeight ?? 0
|
||||
|
||||
clip: true
|
||||
radius: Appearance.rounding.full
|
||||
color: DynamicColors.palette.m3primary
|
||||
|
||||
opacity: todayItem ? 1 : 0
|
||||
scale: todayItem ? 1 : 0.7
|
||||
|
||||
Coloriser {
|
||||
x: -todayIndicator.x
|
||||
y: -todayIndicator.y
|
||||
|
||||
implicitWidth: grid.width
|
||||
implicitHeight: grid.height
|
||||
|
||||
source: grid
|
||||
sourceColor: DynamicColors.palette.m3onSurface
|
||||
colorizationColor: DynamicColors.palette.m3onPrimary
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Coloriser {
|
||||
colorizationColor: DynamicColors.palette.m3onPrimary
|
||||
implicitHeight: grid.height
|
||||
implicitWidth: grid.width
|
||||
source: grid
|
||||
sourceColor: DynamicColors.palette.m3onSurface
|
||||
x: -todayIndicator.x
|
||||
y: -todayIndicator.y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,44 +7,44 @@ import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
implicitWidth: 110
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: 110
|
||||
|
||||
ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 0
|
||||
ColumnLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.bottomMargin: -(font.pointSize * 0.4)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.hourStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: 18
|
||||
font.family: "Rubik"
|
||||
font.weight: 600
|
||||
}
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: -(font.pointSize * 0.4)
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.family: "Rubik"
|
||||
font.pointSize: 18
|
||||
font.weight: 600
|
||||
text: Time.hourStr
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "•••"
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: 18 * 0.9
|
||||
font.family: "Rubik"
|
||||
}
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.family: "Rubik"
|
||||
font.pointSize: 18 * 0.9
|
||||
text: "•••"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: -(font.pointSize * 0.4)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.minuteStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: 18
|
||||
font.family: "Rubik"
|
||||
font.weight: 600
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -(font.pointSize * 0.4)
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.family: "Rubik"
|
||||
font.pointSize: 18
|
||||
font.weight: 600
|
||||
text: Time.minuteStr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+179
-186
@@ -9,230 +9,223 @@ import qs.Modules
|
||||
import qs.Paths
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
property real playerProgress: {
|
||||
const active = Players.active;
|
||||
return active?.length ? active.position / active.length : 0;
|
||||
}
|
||||
property real playerProgress: {
|
||||
const active = Players.active;
|
||||
return active?.length ? active.position / active.length : 0;
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
implicitWidth: Config.dashboard.sizes.mediaWidth
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: Config.dashboard.sizes.mediaWidth
|
||||
|
||||
Behavior on playerProgress {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
Behavior on playerProgress {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: Players.active?.isPlaying ?? false
|
||||
interval: Config.dashboard.mediaUpdateInterval
|
||||
triggeredOnStart: true
|
||||
repeat: true
|
||||
onTriggered: Players.active?.positionChanged()
|
||||
}
|
||||
Timer {
|
||||
interval: Config.dashboard.mediaUpdateInterval
|
||||
repeat: true
|
||||
running: Players.active?.isPlaying ?? false
|
||||
triggeredOnStart: true
|
||||
|
||||
ServiceRef {
|
||||
service: Audio.beatTracker
|
||||
}
|
||||
onTriggered: Players.active?.positionChanged()
|
||||
}
|
||||
|
||||
Shape {
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
ServiceRef {
|
||||
service: Audio.beatTracker
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
Shape {
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
PathAngleArc {
|
||||
centerX: cover.x + cover.width / 2
|
||||
centerY: cover.y + cover.height / 2
|
||||
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
|
||||
sweepAngle: Config.dashboard.sizes.mediaProgressSweep
|
||||
}
|
||||
ShapePath {
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
fillColor: "transparent"
|
||||
strokeColor: DynamicColors.palette.m3primary
|
||||
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
PathAngleArc {
|
||||
centerX: cover.x + cover.width / 2
|
||||
centerY: cover.y + cover.height / 2
|
||||
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
|
||||
sweepAngle: Config.dashboard.sizes.mediaProgressSweep
|
||||
}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: cover.x + cover.width / 2
|
||||
centerY: cover.y + cover.height / 2
|
||||
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
|
||||
sweepAngle: Config.dashboard.sizes.mediaProgressSweep * root.playerProgress
|
||||
}
|
||||
ShapePath {
|
||||
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: DynamicColors.palette.m3primary
|
||||
strokeWidth: Config.dashboard.sizes.mediaProgressThickness
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: cover
|
||||
PathAngleArc {
|
||||
centerX: cover.x + cover.width / 2
|
||||
centerY: cover.y + cover.height / 2
|
||||
radiusX: (cover.width + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
radiusY: (cover.height + Config.dashboard.sizes.mediaProgressThickness) / 2 + Appearance.spacing.small
|
||||
startAngle: -90 - Config.dashboard.sizes.mediaProgressSweep / 2
|
||||
sweepAngle: Config.dashboard.sizes.mediaProgressSweep * root.playerProgress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small
|
||||
CustomClippingRect {
|
||||
id: cover
|
||||
|
||||
implicitHeight: width
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
radius: Infinity
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large + Config.dashboard.sizes.mediaProgressThickness + Appearance.spacing.small
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.tPalette.m3surfaceContainerHigh
|
||||
implicitHeight: width
|
||||
radius: Infinity
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: (parent.width * 0.4) || 1
|
||||
grade: 200
|
||||
text: "art_track"
|
||||
}
|
||||
|
||||
grade: 200
|
||||
text: "art_track"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: (parent.width * 0.4) || 1
|
||||
}
|
||||
Image {
|
||||
id: image
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
CustomText {
|
||||
id: title
|
||||
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: cover.bottom
|
||||
anchors.topMargin: Appearance.spacing.normal
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: title
|
||||
CustomText {
|
||||
id: album
|
||||
|
||||
anchors.top: cover.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Appearance.spacing.normal
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album")
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
}
|
||||
|
||||
animate: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackTitle ?? qsTr("No media")) || qsTr("Unknown title")
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
CustomText {
|
||||
id: artist
|
||||
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: album.bottom
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: album
|
||||
Row {
|
||||
id: controls
|
||||
|
||||
anchors.top: title.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: artist.bottom
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
animate: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackAlbum ?? qsTr("No media")) || qsTr("Unknown album")
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
Control {
|
||||
function onClicked(): void {
|
||||
Players.active?.previous();
|
||||
}
|
||||
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
canUse: Players.active?.canGoPrevious ?? false
|
||||
icon: "skip_previous"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: artist
|
||||
Control {
|
||||
function onClicked(): void {
|
||||
Players.active?.togglePlaying();
|
||||
}
|
||||
|
||||
anchors.top: album.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Appearance.spacing.small
|
||||
canUse: Players.active?.canTogglePlaying ?? false
|
||||
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
|
||||
}
|
||||
|
||||
animate: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: (Players.active?.trackArtist ?? qsTr("No media")) || qsTr("Unknown artist")
|
||||
color: DynamicColors.palette.m3secondary
|
||||
Control {
|
||||
function onClicked(): void {
|
||||
Players.active?.next();
|
||||
}
|
||||
|
||||
width: parent.implicitWidth - Appearance.padding.large * 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
canUse: Players.active?.canGoNext ?? false
|
||||
icon: "skip_next"
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controls
|
||||
component Control: CustomRect {
|
||||
id: control
|
||||
|
||||
anchors.top: artist.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
required property bool canUse
|
||||
required property string icon
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
||||
Control {
|
||||
icon: "skip_previous"
|
||||
canUse: Players.active?.canGoPrevious ?? false
|
||||
implicitHeight: implicitWidth
|
||||
implicitWidth: Math.max(icon.implicitHeight, icon.implicitHeight) + Appearance.padding.small
|
||||
|
||||
function onClicked(): void {
|
||||
Players.active?.previous();
|
||||
}
|
||||
}
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
|
||||
Control {
|
||||
icon: Players.active?.isPlaying ? "pause" : "play_arrow"
|
||||
canUse: Players.active?.canTogglePlaying ?? false
|
||||
disabled: !control.canUse
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
Players.active?.togglePlaying();
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
Control {
|
||||
icon: "skip_next"
|
||||
canUse: Players.active?.canGoNext ?? false
|
||||
|
||||
function onClicked(): void {
|
||||
Players.active?.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Control: CustomRect {
|
||||
id: control
|
||||
|
||||
required property string icon
|
||||
required property bool canUse
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
||||
implicitWidth: Math.max(icon.implicitHeight, icon.implicitHeight) + Appearance.padding.small
|
||||
implicitHeight: implicitWidth
|
||||
|
||||
StateLayer {
|
||||
disabled: !control.canUse
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: font.pointSize * 0.05
|
||||
|
||||
animate: true
|
||||
text: control.icon
|
||||
color: control.canUse ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: font.pointSize * 0.05
|
||||
animate: true
|
||||
color: control.canUse ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: control.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,95 +4,90 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Row {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
padding: Appearance.padding.large
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
padding: Appearance.padding.large
|
||||
spacing: Appearance.spacing.large
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
Resource {
|
||||
color: DynamicColors.palette.m3primary
|
||||
icon: "memory"
|
||||
value: SystemUsage.cpuPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "memory"
|
||||
value: SystemUsage.cpuPerc
|
||||
color: DynamicColors.palette.m3primary
|
||||
}
|
||||
Resource {
|
||||
color: DynamicColors.palette.m3secondary
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
color: DynamicColors.palette.m3secondary
|
||||
}
|
||||
Resource {
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
icon: "gamepad"
|
||||
value: SystemUsage.gpuPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "gamepad"
|
||||
value: SystemUsage.gpuPerc
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
}
|
||||
Resource {
|
||||
color: DynamicColors.palette.m3primary
|
||||
icon: "host"
|
||||
value: SystemUsage.gpuMemUsed
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "host"
|
||||
value: SystemUsage.gpuMemUsed
|
||||
color: DynamicColors.palette.m3primary
|
||||
}
|
||||
Resource {
|
||||
color: DynamicColors.palette.m3secondary
|
||||
icon: "hard_disk"
|
||||
value: SystemUsage.storagePerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
icon: "hard_disk"
|
||||
value: SystemUsage.storagePerc
|
||||
color: DynamicColors.palette.m3secondary
|
||||
}
|
||||
component Resource: Item {
|
||||
id: res
|
||||
|
||||
component Resource: Item {
|
||||
id: res
|
||||
required property color color
|
||||
required property string icon
|
||||
required property real value
|
||||
|
||||
required property string icon
|
||||
required property real value
|
||||
required property color color
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.top: parent.top
|
||||
implicitWidth: icon.implicitWidth
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Appearance.padding.large
|
||||
implicitWidth: icon.implicitWidth
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: icon.top
|
||||
anchors.bottomMargin: Appearance.spacing.small
|
||||
CustomRect {
|
||||
anchors.bottom: icon.top
|
||||
anchors.bottomMargin: Appearance.spacing.small
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitWidth: Config.dashboard.sizes.resourceProgessThickness
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
implicitWidth: Config.dashboard.sizes.resourceProgessThickness
|
||||
CustomRect {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: res.color
|
||||
implicitHeight: res.value * parent.height
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
}
|
||||
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.full
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: res.value * parent.height
|
||||
|
||||
color: res.color
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
text: res.icon
|
||||
color: res.color
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
color: res.color
|
||||
text: res.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,122 +7,116 @@ import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Row {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties state
|
||||
required property PersistentProperties state
|
||||
|
||||
padding: 20
|
||||
spacing: 12
|
||||
padding: 20
|
||||
spacing: 12
|
||||
|
||||
CustomClippingRect {
|
||||
implicitWidth: info.implicitHeight
|
||||
implicitHeight: info.implicitHeight
|
||||
CustomClippingRect {
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: info.implicitHeight
|
||||
implicitWidth: info.implicitHeight
|
||||
radius: 8
|
||||
|
||||
radius: 8
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
font.pointSize: Math.floor(info.implicitHeight / 2) || 1
|
||||
grade: 200
|
||||
text: "person"
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
CachingImage {
|
||||
id: pfp
|
||||
|
||||
text: "person"
|
||||
fill: 1
|
||||
grade: 200
|
||||
font.pointSize: Math.floor(info.implicitHeight / 2) || 1
|
||||
}
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: pfp
|
||||
Column {
|
||||
id: info
|
||||
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 12
|
||||
|
||||
Column {
|
||||
id: info
|
||||
Item {
|
||||
id: line
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 12
|
||||
implicitHeight: Math.max(icon.implicitHeight, text.implicitHeight)
|
||||
implicitWidth: icon.implicitWidth + text.width + text.anchors.leftMargin
|
||||
|
||||
Item {
|
||||
id: line
|
||||
ColoredIcon {
|
||||
id: icon
|
||||
|
||||
implicitWidth: icon.implicitWidth + text.width + text.anchors.leftMargin
|
||||
implicitHeight: Math.max(icon.implicitHeight, text.implicitHeight)
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitSize: Math.floor(13 * 1.34)
|
||||
source: SystemInfo.osLogo
|
||||
}
|
||||
|
||||
ColoredIcon {
|
||||
id: icon
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: icon.anchors.leftMargin
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: 13
|
||||
text: `: ${SystemInfo.osPrettyName || SystemInfo.osName}`
|
||||
width: Config.dashboard.sizes.infoWidth
|
||||
}
|
||||
}
|
||||
|
||||
source: SystemInfo.osLogo
|
||||
implicitSize: Math.floor(13 * 1.34)
|
||||
color: DynamicColors.palette.m3primary
|
||||
}
|
||||
InfoLine {
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
icon: "select_window_2"
|
||||
text: SystemInfo.wm
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
InfoLine {
|
||||
id: uptime
|
||||
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: icon.anchors.leftMargin
|
||||
text: `: ${SystemInfo.osPrettyName || SystemInfo.osName}`
|
||||
font.pointSize: 13
|
||||
colour: DynamicColors.palette.m3tertiary
|
||||
icon: "timer"
|
||||
text: qsTr("up %1").arg(SystemInfo.uptime)
|
||||
}
|
||||
}
|
||||
|
||||
width: Config.dashboard.sizes.infoWidth
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
component InfoLine: Item {
|
||||
id: line
|
||||
|
||||
InfoLine {
|
||||
icon: "select_window_2"
|
||||
text: SystemInfo.wm
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
}
|
||||
required property color colour
|
||||
required property string icon
|
||||
required property string text
|
||||
|
||||
InfoLine {
|
||||
id: uptime
|
||||
implicitHeight: Math.max(icon.implicitHeight, text.implicitHeight)
|
||||
implicitWidth: icon.implicitWidth + text.width + text.anchors.leftMargin
|
||||
|
||||
icon: "timer"
|
||||
text: qsTr("up %1").arg(SystemInfo.uptime)
|
||||
colour: DynamicColors.palette.m3tertiary
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
component InfoLine: Item {
|
||||
id: line
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
|
||||
color: line.colour
|
||||
fill: 1
|
||||
font.pointSize: 13
|
||||
text: line.icon
|
||||
}
|
||||
|
||||
required property string icon
|
||||
required property string text
|
||||
required property color colour
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
implicitWidth: icon.implicitWidth + text.width + text.anchors.leftMargin
|
||||
implicitHeight: Math.max(icon.implicitHeight, text.implicitHeight)
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
|
||||
|
||||
fill: 1
|
||||
text: line.icon
|
||||
color: line.colour
|
||||
font.pointSize: 13
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: icon.anchors.leftMargin
|
||||
text: `: ${line.text}`
|
||||
font.pointSize: 13
|
||||
|
||||
width: Config.dashboard.sizes.infoWidth
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: icon.anchors.leftMargin
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: 13
|
||||
text: `: ${line.text}`
|
||||
width: Config.dashboard.sizes.infoWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,53 +4,47 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: icon.implicitWidth + info.implicitWidth + info.anchors.leftMargin
|
||||
|
||||
implicitWidth: icon.implicitWidth + info.implicitWidth + info.anchors.leftMargin
|
||||
Component.onCompleted: Weather.reload()
|
||||
|
||||
Component.onCompleted: Weather.reload()
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: 54
|
||||
text: Weather.icon
|
||||
}
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
Column {
|
||||
id: info
|
||||
|
||||
animate: true
|
||||
text: Weather.icon
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: 54
|
||||
}
|
||||
|
||||
Column {
|
||||
id: info
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: icon.right
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.large
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
|
||||
spacing: 8
|
||||
CustomText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
text: Weather.temp
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
animate: true
|
||||
text: Weather.temp
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
animate: true
|
||||
text: Weather.description
|
||||
|
||||
elide: Text.ElideRight
|
||||
width: Math.min(implicitWidth, root.parent.width - icon.implicitWidth - info.anchors.leftMargin - 24 * 2)
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
animate: true
|
||||
elide: Text.ElideRight
|
||||
text: Weather.description
|
||||
width: Math.min(implicitWidth, root.parent.width - icon.implicitWidth - info.anchors.leftMargin - 24 * 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+182
-186
@@ -10,237 +10,233 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property real nonAnimWidth
|
||||
required property PersistentProperties state
|
||||
readonly property alias count: bar.count
|
||||
readonly property alias count: bar.count
|
||||
required property real nonAnimWidth
|
||||
required property PersistentProperties state
|
||||
|
||||
implicitHeight: bar.implicitHeight + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
|
||||
implicitHeight: bar.implicitHeight + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
|
||||
|
||||
TabBar {
|
||||
id: bar
|
||||
TabBar {
|
||||
id: bar
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
background: null
|
||||
currentIndex: root.state.currentTab
|
||||
|
||||
currentIndex: root.state.currentTab
|
||||
background: null
|
||||
onCurrentIndexChanged: root.state.currentTab = currentIndex
|
||||
|
||||
onCurrentIndexChanged: root.state.currentTab = currentIndex
|
||||
Tab {
|
||||
iconName: "dashboard"
|
||||
text: qsTr("Dashboard")
|
||||
}
|
||||
|
||||
Tab {
|
||||
iconName: "dashboard"
|
||||
text: qsTr("Dashboard")
|
||||
}
|
||||
Tab {
|
||||
iconName: "queue_music"
|
||||
text: qsTr("Media")
|
||||
}
|
||||
|
||||
Tab {
|
||||
iconName: "queue_music"
|
||||
text: qsTr("Media")
|
||||
}
|
||||
Tab {
|
||||
iconName: "speed"
|
||||
text: qsTr("Performance")
|
||||
}
|
||||
|
||||
Tab {
|
||||
iconName: "speed"
|
||||
text: qsTr("Performance")
|
||||
}
|
||||
Tab {
|
||||
iconName: "cloud"
|
||||
text: qsTr("Weather")
|
||||
}
|
||||
|
||||
Tab {
|
||||
iconName: "cloud"
|
||||
text: qsTr("Weather")
|
||||
}
|
||||
// Tab {
|
||||
// iconName: "workspaces"
|
||||
// text: qsTr("Workspaces")
|
||||
// }
|
||||
}
|
||||
|
||||
// Tab {
|
||||
// iconName: "workspaces"
|
||||
// text: qsTr("Workspaces")
|
||||
// }
|
||||
}
|
||||
Item {
|
||||
id: indicator
|
||||
|
||||
Item {
|
||||
id: indicator
|
||||
anchors.top: bar.bottom
|
||||
clip: true
|
||||
implicitHeight: 40
|
||||
implicitWidth: bar.currentItem.implicitWidth
|
||||
x: {
|
||||
const tab = bar.currentItem;
|
||||
const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count;
|
||||
return width * tab.TabBar.index + (width - tab.implicitWidth) / 2;
|
||||
}
|
||||
|
||||
anchors.top: bar.bottom
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: bar.currentItem.implicitWidth
|
||||
implicitHeight: 40
|
||||
CustomRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitHeight: parent.implicitHeight * 2
|
||||
radius: 1000
|
||||
}
|
||||
}
|
||||
|
||||
x: {
|
||||
const tab = bar.currentItem;
|
||||
const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count;
|
||||
return width * tab.TabBar.index + (width - tab.implicitWidth) / 2;
|
||||
}
|
||||
CustomRect {
|
||||
id: separator
|
||||
|
||||
clip: true
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: indicator.bottom
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: parent.implicitHeight * 2
|
||||
component Tab: TabButton {
|
||||
id: tab
|
||||
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
}
|
||||
readonly property bool current: TabBar.tabBar.currentItem === this
|
||||
required property string iconName
|
||||
|
||||
Behavior on x {
|
||||
Anim {}
|
||||
}
|
||||
background: null
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
contentItem: CustomMouseArea {
|
||||
id: mouse
|
||||
|
||||
CustomRect {
|
||||
id: separator
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (event.angleDelta.y < 0)
|
||||
root.state.currentTab = Math.min(root.state.currentTab + 1, bar.count - 1);
|
||||
else if (event.angleDelta.y > 0)
|
||||
root.state.currentTab = Math.max(root.state.currentTab - 1, 0);
|
||||
}
|
||||
|
||||
anchors.top: indicator.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
implicitHeight: icon.height + label.height
|
||||
implicitWidth: Math.max(icon.width, label.width)
|
||||
|
||||
implicitHeight: 1
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
}
|
||||
onPressed: event => {
|
||||
root.state.currentTab = tab.TabBar.index;
|
||||
|
||||
component Tab: TabButton {
|
||||
id: tab
|
||||
const stateY = stateWrapper.y;
|
||||
rippleAnim.x = event.x;
|
||||
rippleAnim.y = event.y - stateY;
|
||||
|
||||
required property string iconName
|
||||
readonly property bool current: TabBar.tabBar.currentItem === this
|
||||
const dist = (ox, oy) => ox * ox + oy * oy;
|
||||
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y + stateY), dist(event.x, stateWrapper.height - event.y), dist(width - event.x, event.y + stateY), dist(width - event.x, stateWrapper.height - event.y)));
|
||||
|
||||
background: null
|
||||
rippleAnim.restart();
|
||||
}
|
||||
|
||||
contentItem: CustomMouseArea {
|
||||
id: mouse
|
||||
SequentialAnimation {
|
||||
id: rippleAnim
|
||||
|
||||
implicitWidth: Math.max(icon.width, label.width)
|
||||
implicitHeight: icon.height + label.height
|
||||
property real radius
|
||||
property real x
|
||||
property real y
|
||||
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
PropertyAction {
|
||||
property: "x"
|
||||
target: ripple
|
||||
value: rippleAnim.x
|
||||
}
|
||||
|
||||
onPressed: event => {
|
||||
root.state.currentTab = tab.TabBar.index;
|
||||
PropertyAction {
|
||||
property: "y"
|
||||
target: ripple
|
||||
value: rippleAnim.y
|
||||
}
|
||||
|
||||
const stateY = stateWrapper.y;
|
||||
rippleAnim.x = event.x;
|
||||
rippleAnim.y = event.y - stateY;
|
||||
PropertyAction {
|
||||
property: "opacity"
|
||||
target: ripple
|
||||
value: 0.08
|
||||
}
|
||||
|
||||
const dist = (ox, oy) => ox * ox + oy * oy;
|
||||
rippleAnim.radius = Math.sqrt(Math.max(dist(event.x, event.y + stateY), dist(event.x, stateWrapper.height - event.y), dist(width - event.x, event.y + stateY), dist(width - event.x, stateWrapper.height - event.y)));
|
||||
|
||||
rippleAnim.restart();
|
||||
}
|
||||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (event.angleDelta.y < 0)
|
||||
root.state.currentTab = Math.min(root.state.currentTab + 1, bar.count - 1);
|
||||
else if (event.angleDelta.y > 0)
|
||||
root.state.currentTab = Math.max(root.state.currentTab - 1, 0);
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: rippleAnim
|
||||
|
||||
property real x
|
||||
property real y
|
||||
property real radius
|
||||
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "x"
|
||||
value: rippleAnim.x
|
||||
}
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "y"
|
||||
value: rippleAnim.y
|
||||
}
|
||||
PropertyAction {
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
value: 0.08
|
||||
}
|
||||
Anim {
|
||||
target: ripple
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
from: 0
|
||||
to: rippleAnim.radius * 2
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
Anim {
|
||||
target: ripple
|
||||
property: "opacity"
|
||||
to: 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 0
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
target: ripple
|
||||
to: rippleAnim.radius * 2
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
property: "opacity"
|
||||
target: ripple
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
ClippingRectangle {
|
||||
id: stateWrapper
|
||||
ClippingRectangle {
|
||||
id: stateWrapper
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitHeight: parent.height + 8 * 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "transparent"
|
||||
implicitHeight: parent.height + 8 * 2
|
||||
radius: 8
|
||||
|
||||
color: "transparent"
|
||||
radius: 8
|
||||
CustomRect {
|
||||
id: stateLayer
|
||||
|
||||
CustomRect {
|
||||
id: stateLayer
|
||||
anchors.fill: parent
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||
opacity: mouse.pressed ? 0.1 : tab.hovered ? 0.08 : 0
|
||||
|
||||
anchors.fill: parent
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||
opacity: mouse.pressed ? 0.1 : tab.hovered ? 0.08 : 0
|
||||
CustomRect {
|
||||
id: ripple
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||
opacity: 0
|
||||
radius: 1000
|
||||
|
||||
CustomRect {
|
||||
id: ripple
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
radius: 1000
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||
opacity: 0
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.bottom: label.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
fill: tab.current ? 1 : 0
|
||||
font.pointSize: 18
|
||||
text: tab.iconName
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: label.top
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
text: tab.iconName
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
fill: tab.current ? 1 : 0
|
||||
font.pointSize: 18
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
text: tab.text
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: tab.current ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||
text: tab.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,85 +6,83 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
readonly property PersistentProperties dashState: PersistentProperties {
|
||||
property int currentTab
|
||||
property date currentDate: new Date()
|
||||
readonly property PersistentProperties dashState: PersistentProperties {
|
||||
property date currentDate: new Date()
|
||||
property int currentTab
|
||||
|
||||
reloadableId: "dashboardState"
|
||||
}
|
||||
reloadableId: "dashboardState"
|
||||
}
|
||||
readonly property real nonAnimHeight: state === "visible" ? (content.item?.nonAnimHeight ?? 0) : 0
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
readonly property real nonAnimHeight: state === "visible" ? (content.item?.nonAnimHeight ?? 0) : 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
visible: height > 0
|
||||
|
||||
visible: height > 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.dashboard && Config.dashboard.enabled
|
||||
|
||||
onStateChanged: {
|
||||
if (state === "visible" && timer.running) {
|
||||
timer.triggered();
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.dashboard && Config.dashboard.enabled
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
onStateChanged: {
|
||||
if (state === "visible" && timer.running) {
|
||||
timer.triggered();
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
]
|
||||
interval: Appearance.anim.durations.extraLarge
|
||||
running: true
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
onTriggered: {
|
||||
content.active = Qt.binding(() => (root.visibilities.dashboard && Config.dashboard.enabled) || root.visible);
|
||||
content.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
running: true
|
||||
interval: Appearance.anim.durations.extraLarge
|
||||
onTriggered: {
|
||||
content.active = Qt.binding(() => (root.visibilities.dashboard && Config.dashboard.enabled) || root.visible);
|
||||
content.visible = true;
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
active: true
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: false
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
visible: false
|
||||
active: true
|
||||
|
||||
sourceComponent: Content {
|
||||
visibilities: root.visibilities
|
||||
state: root.dashState
|
||||
}
|
||||
}
|
||||
sourceComponent: Content {
|
||||
state: root.dashState
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+176
-180
@@ -8,211 +8,207 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
CustomListView {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property CustomTextField search
|
||||
required property PersistentProperties visibilities
|
||||
required property CustomTextField search
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
highlightFollowsCurrentItem: false
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
|
||||
orientation: Qt.Vertical
|
||||
preferredHighlightBegin: 0
|
||||
preferredHighlightEnd: height
|
||||
spacing: Appearance.spacing.small
|
||||
state: {
|
||||
const text = search.text;
|
||||
const prefix = Config.launcher.actionPrefix;
|
||||
if (text.startsWith(prefix)) {
|
||||
for (const action of ["calc", "scheme", "variant"])
|
||||
if (text.startsWith(`${prefix}${action} `))
|
||||
return action;
|
||||
|
||||
onValuesChanged: root.currentIndex = 0
|
||||
}
|
||||
return "actions";
|
||||
}
|
||||
|
||||
return "apps";
|
||||
}
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
spacing: Appearance.spacing.small
|
||||
orientation: Qt.Vertical
|
||||
implicitHeight: (Config.launcher.sizes.itemHeight + spacing) * Math.min(Config.launcher.maxAppsShown, count) - spacing
|
||||
|
||||
preferredHighlightBegin: 0
|
||||
preferredHighlightEnd: height
|
||||
highlightRangeMode: ListView.ApplyRange
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: root
|
||||
}
|
||||
add: Transition {
|
||||
enabled: !root.state
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
highlight: CustomRect {
|
||||
radius: 8
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
opacity: 0.08
|
||||
Anim {
|
||||
from: 0
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
addDisplaced: Transition {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
property: "y"
|
||||
}
|
||||
|
||||
y: root.currentItem?.y ?? 0
|
||||
implicitWidth: root.width
|
||||
implicitHeight: root.currentItem?.implicitHeight ?? 0
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
highlight: CustomRect {
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
implicitHeight: root.currentItem?.implicitHeight ?? 0
|
||||
implicitWidth: root.width
|
||||
opacity: 0.08
|
||||
radius: 8
|
||||
y: root.currentItem?.y ?? 0
|
||||
|
||||
state: {
|
||||
const text = search.text;
|
||||
const prefix = Config.launcher.actionPrefix;
|
||||
if (text.startsWith(prefix)) {
|
||||
for (const action of ["calc", "scheme", "variant"])
|
||||
if (text.startsWith(`${prefix}${action} `))
|
||||
return action;
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
|
||||
return "actions";
|
||||
}
|
||||
onValuesChanged: root.currentIndex = 0
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
|
||||
return "apps";
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
remove: Transition {
|
||||
enabled: !root.state
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
Anim {
|
||||
from: 1
|
||||
properties: "opacity,scale"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
|
||||
PropertyChanges {
|
||||
model.values: Apps.search(search.text)
|
||||
root.delegate: appItem
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "actions"
|
||||
PropertyChanges {
|
||||
model.values: Apps.search(search.text)
|
||||
root.delegate: appItem
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "actions"
|
||||
|
||||
PropertyChanges {
|
||||
model.values: Actions.query(search.text)
|
||||
root.delegate: actionItem
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "calc"
|
||||
PropertyChanges {
|
||||
model.values: Actions.query(search.text)
|
||||
root.delegate: actionItem
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "calc"
|
||||
|
||||
PropertyChanges {
|
||||
model.values: [0]
|
||||
root.delegate: calcItem
|
||||
}
|
||||
},
|
||||
]
|
||||
PropertyChanges {
|
||||
model.values: [0]
|
||||
root.delegate: calcItem
|
||||
}
|
||||
}
|
||||
]
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
from: 1
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
Anim {
|
||||
target: root
|
||||
property: "scale"
|
||||
from: 1
|
||||
to: 0.9
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
targets: [model, root]
|
||||
properties: "values,delegate"
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
Anim {
|
||||
target: root
|
||||
property: "scale"
|
||||
from: 0.9
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
targets: [root.add, root.remove]
|
||||
property: "enabled"
|
||||
value: true
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
from: 1
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 0.9
|
||||
}
|
||||
}
|
||||
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: root
|
||||
}
|
||||
PropertyAction {
|
||||
properties: "values,delegate"
|
||||
targets: [model, root]
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
enabled: !root.state
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
from: 0.9
|
||||
property: "scale"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
enabled: !root.state
|
||||
PropertyAction {
|
||||
property: "enabled"
|
||||
targets: [root.add, root.remove]
|
||||
value: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
from: 1
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: appItem
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
AppItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
addDisplaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: actionItem
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
ActionItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appItem
|
||||
Component {
|
||||
id: calcItem
|
||||
|
||||
AppItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: actionItem
|
||||
|
||||
ActionItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: calcItem
|
||||
|
||||
CalcItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
CalcItem {
|
||||
list: root
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,56 +4,63 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: Config.barConfig.rounding + 5
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: Config.barConfig.rounding + 5
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.rounding * 2
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
}
|
||||
|
||||
+142
-156
@@ -8,183 +8,169 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property var panels
|
||||
required property real maxHeight
|
||||
required property real maxHeight
|
||||
readonly property int padding: Appearance.padding.small
|
||||
required property var panels
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
readonly property int padding: Appearance.padding.small
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
implicitHeight: searchWrapper.height + listWrapper.height + padding * 2
|
||||
implicitWidth: listWrapper.width + padding * 2
|
||||
|
||||
implicitWidth: listWrapper.width + padding * 2
|
||||
implicitHeight: searchWrapper.height + listWrapper.height + padding * 2
|
||||
Item {
|
||||
id: listWrapper
|
||||
|
||||
Item {
|
||||
id: listWrapper
|
||||
anchors.bottom: searchWrapper.top
|
||||
anchors.bottomMargin: root.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: list.height + root.padding
|
||||
implicitWidth: list.width
|
||||
|
||||
implicitWidth: list.width
|
||||
implicitHeight: list.height + root.padding
|
||||
ContentList {
|
||||
id: list
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: searchWrapper.top
|
||||
anchors.bottomMargin: root.padding
|
||||
content: root
|
||||
maxHeight: root.maxHeight - searchWrapper.implicitHeight - root.padding * 3
|
||||
padding: root.padding
|
||||
panels: root.panels
|
||||
rounding: root.rounding
|
||||
search: search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
ContentList {
|
||||
id: list
|
||||
CustomRect {
|
||||
id: searchWrapper
|
||||
|
||||
content: root
|
||||
visibilities: root.visibilities
|
||||
panels: root.panels
|
||||
maxHeight: root.maxHeight - searchWrapper.implicitHeight - root.padding * 3
|
||||
search: search
|
||||
padding: root.padding
|
||||
rounding: root.rounding
|
||||
}
|
||||
}
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: root.padding
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
|
||||
radius: 8
|
||||
|
||||
CustomRect {
|
||||
id: searchWrapper
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||
radius: 8
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding + 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: "search"
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: root.padding
|
||||
CustomTextField {
|
||||
id: search
|
||||
|
||||
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
|
||||
anchors.left: searchIcon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.right: clearIcon.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
bottomPadding: Appearance.padding.larger
|
||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||
topPadding: Appearance.padding.larger
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||
Keys.onPressed: event => {
|
||||
if (!Config.launcher.vimKeybinds)
|
||||
return;
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding + 10
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_J) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_K) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
|
||||
onAccepted: {
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
|
||||
Wallpapers.previewColourLock = true;
|
||||
Wallpapers.setWallpaper(currentItem.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
} else if (text.startsWith(Config.launcher.actionPrefix)) {
|
||||
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
|
||||
currentItem.onClicked();
|
||||
else
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: "search"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
}
|
||||
Connections {
|
||||
function onLauncherChanged(): void {
|
||||
if (!root.visibilities.launcher)
|
||||
search.text = "";
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: search
|
||||
function onSessionChanged(): void {
|
||||
if (!root.visibilities.session)
|
||||
search.forceActiveFocus();
|
||||
}
|
||||
|
||||
anchors.left: searchIcon.right
|
||||
anchors.right: clearIcon.left
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
target: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
topPadding: Appearance.padding.larger
|
||||
bottomPadding: Appearance.padding.larger
|
||||
MaterialIcon {
|
||||
id: clearIcon
|
||||
|
||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.padding + 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
opacity: {
|
||||
if (!search.text)
|
||||
return 0;
|
||||
if (mouse.pressed)
|
||||
return 0.7;
|
||||
if (mouse.containsMouse)
|
||||
return 0.8;
|
||||
return 1;
|
||||
}
|
||||
text: "close"
|
||||
width: search.text ? implicitWidth : implicitWidth / 2
|
||||
|
||||
onAccepted: {
|
||||
const currentItem = list.currentList?.currentItem;
|
||||
if (currentItem) {
|
||||
if (list.showWallpapers) {
|
||||
if (DynamicColors.scheme === "dynamic" && currentItem.modelData.path !== Wallpapers.actualCurrent)
|
||||
Wallpapers.previewColourLock = true;
|
||||
Wallpapers.setWallpaper(currentItem.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
} else if (text.startsWith(Config.launcher.actionPrefix)) {
|
||||
if (text.startsWith(`${Config.launcher.actionPrefix}calc `))
|
||||
currentItem.onClicked();
|
||||
else
|
||||
currentItem.modelData.onClicked(list.currentList);
|
||||
} else {
|
||||
Apps.launch(currentItem.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onUpPressed: list.currentList?.incrementCurrentIndex()
|
||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||
anchors.fill: parent
|
||||
cursorShape: search.text ? Qt.PointingHandCursor : undefined
|
||||
hoverEnabled: true
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (!Config.launcher.vimKeybinds)
|
||||
return;
|
||||
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_J) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_K) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
list.currentList?.incrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
} else if (event.key === Qt.Key_Backtab || (event.key === Qt.Key_Tab && (event.modifiers & Qt.ShiftModifier))) {
|
||||
list.currentList?.decrementCurrentIndex();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
|
||||
Connections {
|
||||
target: root.visibilities
|
||||
|
||||
function onLauncherChanged(): void {
|
||||
if (!root.visibilities.launcher)
|
||||
search.text = "";
|
||||
}
|
||||
|
||||
function onSessionChanged(): void {
|
||||
if (!root.visibilities.session)
|
||||
search.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: clearIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: root.padding + 10
|
||||
|
||||
width: search.text ? implicitWidth : implicitWidth / 2
|
||||
opacity: {
|
||||
if (!search.text)
|
||||
return 0;
|
||||
if (mouse.pressed)
|
||||
return 0.7;
|
||||
if (mouse.containsMouse)
|
||||
return 0.8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
text: "close"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: search.text ? Qt.PointingHandCursor : undefined
|
||||
|
||||
onClicked: search.text = ""
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: search.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+129
-135
@@ -9,162 +9,156 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var content
|
||||
required property PersistentProperties visibilities
|
||||
required property var panels
|
||||
required property real maxHeight
|
||||
required property CustomTextField search
|
||||
required property int padding
|
||||
required property int rounding
|
||||
required property var content
|
||||
readonly property Item currentList: showWallpapers ? wallpaperList.item : appList.item
|
||||
required property real maxHeight
|
||||
required property int padding
|
||||
required property var panels
|
||||
required property int rounding
|
||||
required property CustomTextField search
|
||||
readonly property bool showWallpapers: search.text.startsWith(`${Config.launcher.actionPrefix}wallpaper `)
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
readonly property bool showWallpapers: search.text.startsWith(`${Config.launcher.actionPrefix}wallpaper `)
|
||||
readonly property Item currentList: showWallpapers ? wallpaperList.item : appList.item
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
clip: true
|
||||
state: showWallpapers ? "wallpapers" : "apps"
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.visibilities.launcher
|
||||
|
||||
clip: true
|
||||
state: showWallpapers ? "wallpapers" : "apps"
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
enabled: root.visibilities.launcher
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on state {
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
from: 1
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Config.launcher.sizes.itemWidth
|
||||
root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight)
|
||||
appList.active: true
|
||||
}
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
anchors.left: root.parent.left
|
||||
anchors.right: root.parent.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "wallpapers"
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "apps"
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Math.max(Config.launcher.sizes.itemWidth * 1.2, wallpaperList.implicitWidth)
|
||||
root.implicitHeight: Config.launcher.sizes.wallpaperHeight
|
||||
wallpaperList.active: true
|
||||
}
|
||||
}
|
||||
]
|
||||
PropertyChanges {
|
||||
appList.active: true
|
||||
root.implicitHeight: Math.min(root.maxHeight, appList.implicitHeight > 0 ? appList.implicitHeight : empty.implicitHeight)
|
||||
root.implicitWidth: Config.launcher.sizes.itemWidth
|
||||
}
|
||||
|
||||
Behavior on state {
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
PropertyAction {}
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
}
|
||||
AnchorChanges {
|
||||
anchors.left: root.parent.left
|
||||
anchors.right: root.parent.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "wallpapers"
|
||||
|
||||
Loader {
|
||||
id: appList
|
||||
PropertyChanges {
|
||||
root.implicitHeight: Config.launcher.sizes.wallpaperHeight
|
||||
root.implicitWidth: Math.max(Config.launcher.sizes.itemWidth * 1.2, wallpaperList.implicitWidth)
|
||||
wallpaperList.active: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
active: false
|
||||
Loader {
|
||||
id: appList
|
||||
|
||||
anchors.fill: parent
|
||||
active: false
|
||||
anchors.fill: parent
|
||||
|
||||
sourceComponent: AppList {
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
sourceComponent: AppList {
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallpaperList
|
||||
Loader {
|
||||
id: wallpaperList
|
||||
|
||||
active: false
|
||||
active: false
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
sourceComponent: WallpaperList {
|
||||
content: root.content
|
||||
panels: root.panels
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: WallpaperList {
|
||||
search: root.search
|
||||
visibilities: root.visibilities
|
||||
panels: root.panels
|
||||
content: root.content
|
||||
}
|
||||
}
|
||||
Row {
|
||||
id: empty
|
||||
|
||||
Row {
|
||||
id: empty
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: root.currentList?.count === 0 ? 1 : 0
|
||||
padding: Appearance.padding.large
|
||||
scale: root.currentList?.count === 0 ? 1 : 0.5
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
opacity: root.currentList?.count === 0 ? 1 : 0
|
||||
scale: root.currentList?.count === 0 ? 1 : 0.5
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
padding: Appearance.padding.large
|
||||
MaterialIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: root.state === "wallpapers" ? "wallpaper_slideshow" : "manage_search"
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MaterialIcon {
|
||||
text: root.state === "wallpapers" ? "wallpaper_slideshow" : "manage_search"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
CustomText {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.weight: 500
|
||||
text: root.state === "wallpapers" ? qsTr("No wallpapers found") : qsTr("No results")
|
||||
}
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
CustomText {
|
||||
text: root.state === "wallpapers" ? qsTr("No wallpapers found") : qsTr("No results")
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
CustomText {
|
||||
text: root.state === "wallpapers" && Wallpapers.list.length === 0 ? qsTr("Try putting some wallpapers in %1").arg(Paths.shortenHome(Paths.wallsdir)) : qsTr("Try searching for something else")
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: root.visibilities.launcher
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.visibilities.launcher
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: root.state === "wallpapers" && Wallpapers.list.length === 0 ? qsTr("Try putting some wallpapers in %1").arg(Paths.shortenHome(Paths.wallsdir)) : qsTr("Try searching for something else")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,66 +5,61 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var modelData
|
||||
required property var list
|
||||
required property var list
|
||||
required property var modelData
|
||||
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.modelData?.onClicked(root.list);
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.modelData?.onClicked(root.list);
|
||||
}
|
||||
}
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: root.modelData?.icon ?? ""
|
||||
}
|
||||
|
||||
text: root.modelData?.icon ?? ""
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
Item {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
implicitHeight: name.implicitHeight + desc.implicitHeight
|
||||
implicitWidth: parent.width - icon.width
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
CustomText {
|
||||
id: name
|
||||
|
||||
Item {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: root.modelData?.name ?? ""
|
||||
}
|
||||
|
||||
implicitWidth: parent.width - icon.width
|
||||
implicitHeight: name.implicitHeight + desc.implicitHeight
|
||||
CustomText {
|
||||
id: desc
|
||||
|
||||
CustomText {
|
||||
id: name
|
||||
|
||||
text: root.modelData?.name ?? ""
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: desc
|
||||
|
||||
text: root.modelData?.desc ?? ""
|
||||
font.pointSize: Appearance.font.size.small
|
||||
color: DynamicColors.palette.m3outline
|
||||
|
||||
elide: Text.ElideRight
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
|
||||
anchors.top: name.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.top: name.bottom
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.modelData?.desc ?? ""
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,67 +8,62 @@ import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property DesktopEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
required property DesktopEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
Apps.launch(root.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: 8
|
||||
radius: 8
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
Apps.launch(root.modelData);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Appearance.padding.larger
|
||||
anchors.rightMargin: Appearance.padding.larger
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
IconImage {
|
||||
id: icon
|
||||
|
||||
IconImage {
|
||||
id: icon
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitSize: parent.height
|
||||
source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
|
||||
}
|
||||
|
||||
source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
|
||||
implicitSize: parent.height
|
||||
Item {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
implicitHeight: name.implicitHeight + comment.implicitHeight
|
||||
implicitWidth: parent.width - icon.width
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
CustomText {
|
||||
id: name
|
||||
|
||||
Item {
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: Appearance.spacing.normal
|
||||
anchors.verticalCenter: icon.verticalCenter
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: root.modelData?.name ?? ""
|
||||
}
|
||||
|
||||
implicitWidth: parent.width - icon.width
|
||||
implicitHeight: name.implicitHeight + comment.implicitHeight
|
||||
CustomText {
|
||||
id: comment
|
||||
|
||||
CustomText {
|
||||
id: name
|
||||
|
||||
text: root.modelData?.name ?? ""
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: comment
|
||||
|
||||
text: (root.modelData?.comment || root.modelData?.genericName || root.modelData?.name) ?? ""
|
||||
font.pointSize: Appearance.font.size.small
|
||||
color: DynamicColors.palette.m3outline
|
||||
|
||||
elide: Text.ElideRight
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
|
||||
anchors.top: name.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.top: name.bottom
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: (root.modelData?.comment || root.modelData?.genericName || root.modelData?.name) ?? ""
|
||||
width: root.width - icon.width - Appearance.rounding.normal * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,117 +8,109 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var list
|
||||
readonly property string math: list.search.text.slice(`${Config.launcher.actionPrefix}calc `.length)
|
||||
required property var list
|
||||
readonly property string math: list.search.text.slice(`${Config.launcher.actionPrefix}calc `.length)
|
||||
|
||||
function onClicked(): void {
|
||||
Quickshell.execDetached(["wl-copy", Qalculator.eval(math, false)]);
|
||||
root.list.visibilities.launcher = false;
|
||||
}
|
||||
function onClicked(): void {
|
||||
Quickshell.execDetached(["wl-copy", Qalculator.eval(math, false)]);
|
||||
root.list.visibilities.launcher = false;
|
||||
}
|
||||
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: Config.launcher.sizes.itemHeight
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.onClicked();
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.onClicked();
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.larger
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Appearance.padding.larger
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
text: "function"
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
CustomText {
|
||||
id: result
|
||||
|
||||
MaterialIcon {
|
||||
text: "function"
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
color: {
|
||||
if (text.includes("error: ") || text.includes("warning: "))
|
||||
return DynamicColors.palette.m3error;
|
||||
if (!root.math)
|
||||
return DynamicColors.palette.m3onSurfaceVariant;
|
||||
return DynamicColors.palette.m3onSurface;
|
||||
}
|
||||
elide: Text.ElideLeft
|
||||
text: root.math.length > 0 ? Qalculator.eval(root.math) : qsTr("Type an expression to calculate")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: result
|
||||
CustomRect {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
clip: true
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
implicitHeight: Math.max(label.implicitHeight, icon.implicitHeight) + Appearance.padding.small * 2
|
||||
implicitWidth: (stateLayer.containsMouse ? label.implicitWidth + label.anchors.rightMargin : 0) + icon.implicitWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
color: {
|
||||
if (text.includes("error: ") || text.includes("warning: "))
|
||||
return DynamicColors.palette.m3error;
|
||||
if (!root.math)
|
||||
return DynamicColors.palette.m3onSurfaceVariant;
|
||||
return DynamicColors.palette.m3onSurface;
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
text: root.math.length > 0 ? Qalculator.eval(root.math) : qsTr("Type an expression to calculate")
|
||||
elide: Text.ElideLeft
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
function onClicked(): void {
|
||||
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.terminal, "fish", "-C", `exec qalc -i '${root.math}'`]);
|
||||
root.list.visibilities.launcher = false;
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
radius: Appearance.rounding.normal
|
||||
clip: true
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
}
|
||||
|
||||
implicitWidth: (stateLayer.containsMouse ? label.implicitWidth + label.anchors.rightMargin : 0) + icon.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: Math.max(label.implicitHeight, icon.implicitHeight) + Appearance.padding.small * 2
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.right: icon.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: stateLayer.containsMouse ? 1 : 0
|
||||
text: qsTr("Open in calculator")
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
function onClicked(): void {
|
||||
Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.terminal, "fish", "-C", `exec qalc -i '${root.math}'`]);
|
||||
root.list.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: icon.left
|
||||
anchors.rightMargin: Appearance.spacing.small
|
||||
|
||||
text: qsTr("Open in calculator")
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
|
||||
opacity: stateLayer.containsMouse ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
|
||||
text: "open_in_new"
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onTertiary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: "open_in_new"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,92 +7,90 @@ import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property FileSystemEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
required property FileSystemEntry modelData
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
scale: 0.5
|
||||
opacity: 0
|
||||
z: PathView.z ?? 0
|
||||
implicitHeight: image.height + label.height + Appearance.spacing.small / 2 + Appearance.padding.large + Appearance.padding.normal
|
||||
implicitWidth: image.width + Appearance.padding.larger * 2
|
||||
opacity: 0
|
||||
scale: 0.5
|
||||
z: PathView.z ?? 0
|
||||
|
||||
Component.onCompleted: {
|
||||
scale = Qt.binding(() => PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0);
|
||||
opacity = Qt.binding(() => PathView.onPath ? 1 : 0);
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: image.width + Appearance.padding.larger * 2
|
||||
implicitHeight: image.height + label.height + Appearance.spacing.small / 2 + Appearance.padding.large + Appearance.padding.normal
|
||||
Component.onCompleted: {
|
||||
scale = Qt.binding(() => PathView.isCurrentItem ? 1 : PathView.onPath ? 0.8 : 0);
|
||||
opacity = Qt.binding(() => PathView.onPath ? 1 : 0);
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
console.log(root.modelData.path);
|
||||
Wallpapers.setWallpaper(root.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
}
|
||||
Wallpapers.setWallpaper(root.modelData.path);
|
||||
root.visibilities.launcher = false;
|
||||
}
|
||||
|
||||
Elevation {
|
||||
anchors.fill: image
|
||||
radius: image.radius
|
||||
opacity: root.PathView.isCurrentItem ? 1 : 0
|
||||
level: 4
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
Elevation {
|
||||
anchors.fill: image
|
||||
level: 4
|
||||
opacity: root.PathView.isCurrentItem ? 1 : 0
|
||||
radius: image.radius
|
||||
|
||||
CustomClippingRect {
|
||||
id: image
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: Appearance.padding.large
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
CustomClippingRect {
|
||||
id: image
|
||||
|
||||
implicitWidth: Config.launcher.sizes.wallpaperWidth
|
||||
implicitHeight: implicitWidth / 16 * 9
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: implicitWidth / 16 * 9
|
||||
implicitWidth: Config.launcher.sizes.wallpaperWidth
|
||||
radius: Appearance.rounding.normal
|
||||
y: Appearance.padding.large
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "image"
|
||||
color: DynamicColors.tPalette.m3outline
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2
|
||||
font.weight: 600
|
||||
}
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.tPalette.m3outline
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2
|
||||
font.weight: 600
|
||||
text: "image"
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
path: root.modelData.path
|
||||
smooth: !root.PathView.view.moving
|
||||
cache: true
|
||||
CachingImage {
|
||||
anchors.fill: parent
|
||||
cache: true
|
||||
path: root.modelData.path
|
||||
smooth: !root.PathView.view.moving
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
anchors.top: image.bottom
|
||||
anchors.topMargin: Appearance.spacing.small / 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
width: image.width - Appearance.padding.normal * 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
renderType: Text.QtRendering
|
||||
text: root.modelData.relativePath
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: image.bottom
|
||||
anchors.topMargin: Appearance.spacing.small / 2
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
renderType: Text.QtRendering
|
||||
text: root.modelData.relativePath
|
||||
width: image.width - Appearance.padding.normal * 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,45 +7,46 @@ import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Searcher {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
function transformSearch(search: string): string {
|
||||
return search.slice(Config.launcher.actionPrefix.length);
|
||||
}
|
||||
function transformSearch(search: string): string {
|
||||
return search.slice(Config.launcher.actionPrefix.length);
|
||||
}
|
||||
|
||||
list: variants.instances
|
||||
useFuzzy: Config.launcher.useFuzzy.actions
|
||||
list: variants.instances
|
||||
useFuzzy: Config.launcher.useFuzzy.actions
|
||||
|
||||
Variants {
|
||||
id: variants
|
||||
Variants {
|
||||
id: variants
|
||||
|
||||
model: Config.launcher.actions.filter(a => (a.enabled ?? true))
|
||||
model: Config.launcher.actions.filter(a => (a.enabled ?? true))
|
||||
|
||||
Action {}
|
||||
}
|
||||
Action {
|
||||
}
|
||||
}
|
||||
|
||||
component Action: QtObject {
|
||||
required property var modelData
|
||||
readonly property string name: modelData.name ?? qsTr("Unnamed")
|
||||
readonly property string desc: modelData.description ?? qsTr("No description")
|
||||
readonly property string icon: modelData.icon ?? "help_outline"
|
||||
readonly property list<string> command: modelData.command ?? []
|
||||
readonly property bool enabled: modelData.enabled ?? true
|
||||
readonly property bool dangerous: modelData.dangerous ?? false
|
||||
component Action: QtObject {
|
||||
readonly property list<string> command: modelData.command ?? []
|
||||
readonly property bool dangerous: modelData.dangerous ?? false
|
||||
readonly property string desc: modelData.description ?? qsTr("No description")
|
||||
readonly property bool enabled: modelData.enabled ?? true
|
||||
readonly property string icon: modelData.icon ?? "help_outline"
|
||||
required property var modelData
|
||||
readonly property string name: modelData.name ?? qsTr("Unnamed")
|
||||
|
||||
function onClicked(list: AppList): void {
|
||||
if (command.length === 0)
|
||||
return;
|
||||
function onClicked(list: AppList): void {
|
||||
if (command.length === 0)
|
||||
return;
|
||||
|
||||
if (command[0] === "autocomplete" && command.length > 1) {
|
||||
list.search.text = `${Config.launcher.actionPrefix}${command[1]} `;
|
||||
} else if (command[0] === "setMode" && command.length > 1) {
|
||||
list.visibilities.launcher = false;
|
||||
Colours.setMode(command[1]);
|
||||
} else {
|
||||
list.visibilities.launcher = false;
|
||||
Quickshell.execDetached(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (command[0] === "autocomplete" && command.length > 1) {
|
||||
list.search.text = `${Config.launcher.actionPrefix}${command[1]} `;
|
||||
} else if (command[0] === "setMode" && command.length > 1) {
|
||||
list.visibilities.launcher = false;
|
||||
Colours.setMode(command[1]);
|
||||
} else {
|
||||
list.visibilities.launcher = false;
|
||||
Quickshell.execDetached(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,72 +7,72 @@ import qs.Helpers
|
||||
import qs.Paths
|
||||
|
||||
Searcher {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
function launch(entry: DesktopEntry): void {
|
||||
appDb.incrementFrequency(entry.id);
|
||||
function launch(entry: DesktopEntry): void {
|
||||
appDb.incrementFrequency(entry.id);
|
||||
|
||||
if (entry.runInTerminal)
|
||||
Quickshell.execDetached({
|
||||
command: ["app2unit", "--", ...Config.general.apps.terminal, `${Quickshell.shellDir}/assets/wrap_term_launch.sh`, ...entry.command],
|
||||
workingDirectory: entry.workingDirectory
|
||||
});
|
||||
else
|
||||
Quickshell.execDetached({
|
||||
command: ["app2unit", "--", ...entry.command],
|
||||
workingDirectory: entry.workingDirectory
|
||||
});
|
||||
}
|
||||
if (entry.runInTerminal)
|
||||
Quickshell.execDetached({
|
||||
command: ["app2unit", "--", ...Config.general.apps.terminal, `${Quickshell.shellDir}/assets/wrap_term_launch.sh`, ...entry.command],
|
||||
workingDirectory: entry.workingDirectory
|
||||
});
|
||||
else
|
||||
Quickshell.execDetached({
|
||||
command: ["app2unit", "--", ...entry.command],
|
||||
workingDirectory: entry.workingDirectory
|
||||
});
|
||||
}
|
||||
|
||||
function search(search: string): list<var> {
|
||||
const prefix = Config.launcher.specialPrefix;
|
||||
function search(search: string): list<var> {
|
||||
const prefix = Config.launcher.specialPrefix;
|
||||
|
||||
if (search.startsWith(`${prefix}i `)) {
|
||||
keys = ["id", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}c `)) {
|
||||
keys = ["categories", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}d `)) {
|
||||
keys = ["comment", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}e `)) {
|
||||
keys = ["execString", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}w `)) {
|
||||
keys = ["startupClass", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}g `)) {
|
||||
keys = ["genericName", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}k `)) {
|
||||
keys = ["keywords", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else {
|
||||
keys = ["name"];
|
||||
weights = [1];
|
||||
if (search.startsWith(`${prefix}i `)) {
|
||||
keys = ["id", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}c `)) {
|
||||
keys = ["categories", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}d `)) {
|
||||
keys = ["comment", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}e `)) {
|
||||
keys = ["execString", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}w `)) {
|
||||
keys = ["startupClass", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}g `)) {
|
||||
keys = ["genericName", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else if (search.startsWith(`${prefix}k `)) {
|
||||
keys = ["keywords", "name"];
|
||||
weights = [0.9, 0.1];
|
||||
} else {
|
||||
keys = ["name"];
|
||||
weights = [1];
|
||||
|
||||
if (!search.startsWith(`${prefix}t `))
|
||||
return query(search).map(e => e.entry);
|
||||
}
|
||||
if (!search.startsWith(`${prefix}t `))
|
||||
return query(search).map(e => e.entry);
|
||||
}
|
||||
|
||||
const results = query(search.slice(prefix.length + 2)).map(e => e.entry);
|
||||
if (search.startsWith(`${prefix}t `))
|
||||
return results.filter(a => a.runInTerminal);
|
||||
return results;
|
||||
}
|
||||
const results = query(search.slice(prefix.length + 2)).map(e => e.entry);
|
||||
if (search.startsWith(`${prefix}t `))
|
||||
return results.filter(a => a.runInTerminal);
|
||||
return results;
|
||||
}
|
||||
|
||||
function selector(item: var): string {
|
||||
return keys.map(k => item[k]).join(" ");
|
||||
}
|
||||
function selector(item: var): string {
|
||||
return keys.map(k => item[k]).join(" ");
|
||||
}
|
||||
|
||||
list: appDb.apps
|
||||
useFuzzy: Config.launcher.useFuzzy.apps
|
||||
list: appDb.apps
|
||||
useFuzzy: Config.launcher.useFuzzy.apps
|
||||
|
||||
AppDb {
|
||||
id: appDb
|
||||
AppDb {
|
||||
id: appDb
|
||||
|
||||
path: `${Paths.state}/apps.sqlite`
|
||||
entries: DesktopEntries.applications.values
|
||||
}
|
||||
entries: DesktopEntries.applications.values
|
||||
path: `${Paths.state}/apps.sqlite`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,90 +8,88 @@ import qs.Config
|
||||
import qs.Modules.Launcher.Items
|
||||
|
||||
PathView {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property CustomTextField search
|
||||
required property var visibilities
|
||||
required property var panels
|
||||
required property var content
|
||||
required property var content
|
||||
readonly property int itemWidth: Config.launcher.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2
|
||||
readonly property int numItems: {
|
||||
const screen = QsWindow.window?.screen;
|
||||
if (!screen)
|
||||
return 0;
|
||||
|
||||
readonly property int itemWidth: Config.launcher.sizes.wallpaperWidth * 0.8 + Appearance.padding.larger * 2
|
||||
// Screen width - 4x outer rounding - 2x max side thickness (cause centered)
|
||||
const barMargins = panels.bar.implicitWidth;
|
||||
let outerMargins = 0;
|
||||
if (panels.popouts.hasCurrent && panels.popouts.currentCenter + panels.popouts.nonAnimHeight / 2 > screen.height - content.implicitHeight)
|
||||
outerMargins = panels.popouts.nonAnimWidth;
|
||||
if ((visibilities.utilities || visibilities.sidebar) && panels.utilities.implicitWidth > outerMargins)
|
||||
outerMargins = panels.utilities.implicitWidth;
|
||||
const maxWidth = screen.width - Config.barConfig.rounding * 4 - (barMargins + outerMargins) * 2;
|
||||
|
||||
readonly property int numItems: {
|
||||
const screen = QsWindow.window?.screen;
|
||||
if (!screen)
|
||||
return 0;
|
||||
if (maxWidth <= 0)
|
||||
return 0;
|
||||
|
||||
// Screen width - 4x outer rounding - 2x max side thickness (cause centered)
|
||||
const barMargins = panels.bar.implicitWidth;
|
||||
let outerMargins = 0;
|
||||
if (panels.popouts.hasCurrent && panels.popouts.currentCenter + panels.popouts.nonAnimHeight / 2 > screen.height - content.implicitHeight)
|
||||
outerMargins = panels.popouts.nonAnimWidth;
|
||||
if ((visibilities.utilities || visibilities.sidebar) && panels.utilities.implicitWidth > outerMargins)
|
||||
outerMargins = panels.utilities.implicitWidth;
|
||||
const maxWidth = screen.width - Config.barConfig.rounding * 4 - (barMargins + outerMargins) * 2;
|
||||
const maxItemsOnScreen = Math.floor(maxWidth / itemWidth);
|
||||
const visible = Math.min(maxItemsOnScreen, Config.launcher.maxWallpapers, scriptModel.values.length);
|
||||
|
||||
if (maxWidth <= 0)
|
||||
return 0;
|
||||
if (visible === 2)
|
||||
return 1;
|
||||
if (visible > 1 && visible % 2 === 0)
|
||||
return visible - 1;
|
||||
return visible;
|
||||
}
|
||||
required property var panels
|
||||
required property CustomTextField search
|
||||
required property var visibilities
|
||||
|
||||
const maxItemsOnScreen = Math.floor(maxWidth / itemWidth);
|
||||
const visible = Math.min(maxItemsOnScreen, Config.launcher.maxWallpapers, scriptModel.values.length);
|
||||
cacheItemCount: 4
|
||||
highlightRangeMode: PathView.StrictlyEnforceRange
|
||||
implicitWidth: Math.min(numItems, count) * itemWidth
|
||||
pathItemCount: numItems
|
||||
preferredHighlightBegin: 0.5
|
||||
preferredHighlightEnd: 0.5
|
||||
snapMode: PathView.SnapToItem
|
||||
|
||||
if (visible === 2)
|
||||
return 1;
|
||||
if (visible > 1 && visible % 2 === 0)
|
||||
return visible - 1;
|
||||
return visible;
|
||||
}
|
||||
delegate: WallpaperItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
model: ScriptModel {
|
||||
id: scriptModel
|
||||
|
||||
model: ScriptModel {
|
||||
id: scriptModel
|
||||
readonly property string search: root.search.text.split(" ").slice(1).join(" ")
|
||||
|
||||
readonly property string search: root.search.text.split(" ").slice(1).join(" ")
|
||||
values: Wallpapers.query(search)
|
||||
|
||||
values: Wallpapers.query(search)
|
||||
onValuesChanged: root.currentIndex = search ? 0 : values.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
}
|
||||
onValuesChanged: root.currentIndex = search ? 0 : values.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
}
|
||||
path: Path {
|
||||
startY: root.height / 2
|
||||
|
||||
Component.onCompleted: currentIndex = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
Component.onDestruction: Wallpapers.stopPreview()
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 0
|
||||
}
|
||||
|
||||
onCurrentItemChanged: {
|
||||
if (currentItem)
|
||||
Wallpapers.preview(currentItem.modelData.path);
|
||||
}
|
||||
PathLine {
|
||||
relativeY: 0
|
||||
x: root.width / 2
|
||||
}
|
||||
|
||||
implicitWidth: Math.min(numItems, count) * itemWidth
|
||||
pathItemCount: numItems
|
||||
cacheItemCount: 4
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 1
|
||||
}
|
||||
|
||||
snapMode: PathView.SnapToItem
|
||||
preferredHighlightBegin: 0.5
|
||||
preferredHighlightEnd: 0.5
|
||||
highlightRangeMode: PathView.StrictlyEnforceRange
|
||||
PathLine {
|
||||
relativeY: 0
|
||||
x: root.width
|
||||
}
|
||||
}
|
||||
|
||||
delegate: WallpaperItem {
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
path: Path {
|
||||
startY: root.height / 2
|
||||
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 0
|
||||
}
|
||||
PathLine {
|
||||
x: root.width / 2
|
||||
relativeY: 0
|
||||
}
|
||||
PathAttribute {
|
||||
name: "z"
|
||||
value: 1
|
||||
}
|
||||
PathLine {
|
||||
x: root.width
|
||||
relativeY: 0
|
||||
}
|
||||
}
|
||||
Component.onCompleted: currentIndex = Wallpapers.list.findIndex(w => w.path === Wallpapers.actualCurrent)
|
||||
Component.onDestruction: Wallpapers.stopPreview()
|
||||
onCurrentItemChanged: {
|
||||
if (currentItem)
|
||||
Wallpapers.preview(currentItem.modelData.path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,125 +6,125 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property PersistentProperties visibilities
|
||||
required property var panels
|
||||
property int contentHeight
|
||||
readonly property real maxHeight: {
|
||||
let max = screen.height - Appearance.spacing.large;
|
||||
if (visibilities.dashboard)
|
||||
max -= panels.dashboard.nonAnimHeight;
|
||||
return max;
|
||||
}
|
||||
required property var panels
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.launcher
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
readonly property bool shouldBeActive: visibilities.launcher
|
||||
property int contentHeight
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
visible: height > 0
|
||||
|
||||
readonly property real maxHeight: {
|
||||
let max = screen.height - Appearance.spacing.large;
|
||||
if (visibilities.dashboard)
|
||||
max -= panels.dashboard.nonAnimHeight;
|
||||
return max;
|
||||
}
|
||||
onMaxHeightChanged: timer.start()
|
||||
onShouldBeActiveChanged: {
|
||||
if (shouldBeActive) {
|
||||
timer.stop();
|
||||
hideAnim.stop();
|
||||
showAnim.start();
|
||||
} else {
|
||||
showAnim.stop();
|
||||
hideAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
onMaxHeightChanged: timer.start()
|
||||
SequentialAnimation {
|
||||
id: showAnim
|
||||
|
||||
visible: height > 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: content.implicitWidth
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
to: root.contentHeight
|
||||
}
|
||||
|
||||
onShouldBeActiveChanged: {
|
||||
if (shouldBeActive) {
|
||||
timer.stop();
|
||||
hideAnim.stop();
|
||||
showAnim.start();
|
||||
} else {
|
||||
showAnim.stop();
|
||||
hideAnim.start();
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: root.implicitHeight = Qt.binding(() => content.implicitHeight)
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: showAnim
|
||||
SequentialAnimation {
|
||||
id: hideAnim
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
to: root.contentHeight
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
ScriptAction {
|
||||
script: root.implicitHeight = Qt.binding(() => content.implicitHeight)
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: root.implicitHeight = root.implicitHeight
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: hideAnim
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
ScriptAction {
|
||||
script: root.implicitHeight = root.implicitHeight
|
||||
}
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
to: 0
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onEnabledChanged(): void {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config.launcher
|
||||
function onMaxShownChanged(): void {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
function onEnabledChanged(): void {
|
||||
timer.start();
|
||||
}
|
||||
target: Config.launcher
|
||||
}
|
||||
|
||||
function onMaxShownChanged(): void {
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onValuesChanged(): void {
|
||||
if (DesktopEntries.applications.values.length < Config.launcher.maxAppsShown)
|
||||
timer.start();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries.applications
|
||||
target: DesktopEntries.applications
|
||||
}
|
||||
|
||||
function onValuesChanged(): void {
|
||||
if (DesktopEntries.applications.values.length < Config.launcher.maxAppsShown)
|
||||
timer.start();
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: Appearance.anim.durations.small
|
||||
|
||||
interval: Appearance.anim.durations.small
|
||||
onRunningChanged: {
|
||||
if (running && !root.shouldBeActive) {
|
||||
content.visible = false;
|
||||
content.active = true;
|
||||
} else {
|
||||
root.contentHeight = Math.min(root.maxHeight, content.implicitHeight);
|
||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||
content.visible = true;
|
||||
if (showAnim.running) {
|
||||
showAnim.stop();
|
||||
showAnim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onRunningChanged: {
|
||||
if (running && !root.shouldBeActive) {
|
||||
content.visible = false;
|
||||
content.active = true;
|
||||
} else {
|
||||
root.contentHeight = Math.min(root.maxHeight, content.implicitHeight);
|
||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||
content.visible = true;
|
||||
if (showAnim.running) {
|
||||
showAnim.stop();
|
||||
showAnim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: false
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
visible: false
|
||||
|
||||
visible: false
|
||||
active: false
|
||||
Component.onCompleted: timer.start()
|
||||
sourceComponent: Content {
|
||||
maxHeight: root.maxHeight
|
||||
panels: root.panels
|
||||
visibilities: root.visibilities
|
||||
|
||||
sourceComponent: Content {
|
||||
visibilities: root.visibilities
|
||||
panels: root.panels
|
||||
maxHeight: root.maxHeight
|
||||
Component.onCompleted: root.contentHeight = implicitHeight
|
||||
}
|
||||
|
||||
Component.onCompleted: root.contentHeight = implicitHeight
|
||||
}
|
||||
}
|
||||
Component.onCompleted: timer.start()
|
||||
}
|
||||
}
|
||||
|
||||
+378
-387
@@ -9,391 +9,382 @@ import qs.Config
|
||||
import qs.Modules
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
|
||||
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
||||
|
||||
Layout.preferredWidth: centerWidth
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: true
|
||||
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.hourStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: ":"
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.minuteStr
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
font.family: Appearance.font.family.clock
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -Appearance.padding.large * 2
|
||||
|
||||
text: Time.format("dddd, d MMMM yyyy")
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * root.centerScale)
|
||||
font.family: Appearance.font.family.mono
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.topMargin: Appearance.spacing.large * 2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
implicitWidth: root.centerWidth / 2
|
||||
implicitHeight: root.centerWidth / 2
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: "person"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Math.floor(root.centerWidth / 4)
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: pfp
|
||||
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
implicitWidth: root.centerWidth * 0.8
|
||||
implicitHeight: input.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
focus: true
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus)
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (root.lock.unlocking)
|
||||
return;
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
|
||||
inputField.placeholder.animate = false;
|
||||
|
||||
root.lock.pam.handleKey(event);
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
hoverEnabled: false
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
||||
function onClicked(): void {
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Item {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
MaterialIcon {
|
||||
id: fprintIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
||||
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
CircularIndicator {
|
||||
anchors.fill: parent
|
||||
running: root.lock.pam.passwd.active
|
||||
}
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: inputField
|
||||
|
||||
pam: root.lock.pam
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
StateLayer {
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
|
||||
function onClicked(): void {
|
||||
root.lock.pam.passwd.start();
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: enterIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "arrow_forward"
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.spacing.large
|
||||
|
||||
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: stateMessage
|
||||
|
||||
readonly property string msg: {
|
||||
if (Hypr.kbLayout !== Hypr.defaultKbLayout) {
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.\nKeyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
return qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
}
|
||||
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.");
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON.");
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON.");
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
property bool shouldBeVisible
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
} else {
|
||||
text = msg;
|
||||
}
|
||||
shouldBeVisible = true;
|
||||
} else {
|
||||
shouldBeVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
scale: shouldBeVisible && !message.msg ? 1 : 0.7
|
||||
opacity: shouldBeVisible && !message.msg ? 1 : 0
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
animateProp: "opacity"
|
||||
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
lineHeight: 1.2
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: message
|
||||
|
||||
readonly property Pam pam: root.lock.pam
|
||||
readonly property string msg: {
|
||||
if (pam.fprintState === "error")
|
||||
return qsTr("FP ERROR: %1").arg(pam.fprint.message);
|
||||
if (pam.state === "error")
|
||||
return qsTr("PW ERROR: %1").arg(pam.passwd.message);
|
||||
|
||||
if (pam.lockMessage)
|
||||
return pam.lockMessage;
|
||||
|
||||
if (pam.state === "max" && pam.fprintState === "max")
|
||||
return qsTr("Maximum password and fingerprint attempts reached.");
|
||||
if (pam.state === "max") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Maximum password attempts reached. Please use fingerprint.");
|
||||
return qsTr("Maximum password attempts reached.");
|
||||
}
|
||||
if (pam.fprintState === "max")
|
||||
return qsTr("Maximum fingerprint attempts reached. Please use password.");
|
||||
|
||||
if (pam.state === "fail") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Incorrect password. Please try again or use fingerprint.");
|
||||
return qsTr("Incorrect password. Please try again.");
|
||||
}
|
||||
if (pam.fprintState === "fail")
|
||||
return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
scale: 0.7
|
||||
opacity: 0
|
||||
color: DynamicColors.palette.m3error
|
||||
|
||||
font.pointSize: Appearance.font.size.small
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
|
||||
exitAnim.stop();
|
||||
if (scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
} else {
|
||||
text = msg;
|
||||
exitAnim.stop();
|
||||
appearAnim.restart();
|
||||
}
|
||||
} else {
|
||||
appearAnim.stop();
|
||||
flashAnim.stop();
|
||||
exitAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.lock.pam
|
||||
|
||||
function onFlashMsg(): void {
|
||||
exitAnim.stop();
|
||||
if (message.scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: appearAnim
|
||||
|
||||
target: message
|
||||
properties: "scale,opacity"
|
||||
to: 1
|
||||
onFinished: flashAnim.restart()
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: flashAnim
|
||||
|
||||
loops: 2
|
||||
|
||||
FlashAnim {
|
||||
to: 0.3
|
||||
}
|
||||
FlashAnim {
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: exitAnim
|
||||
|
||||
Anim {
|
||||
target: message
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
Anim {
|
||||
target: message
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component FlashAnim: NumberAnimation {
|
||||
target: message
|
||||
property: "opacity"
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.Linear
|
||||
}
|
||||
id: root
|
||||
|
||||
readonly property real centerScale: Math.min(1, (lock.screen?.height ?? 1440) / 1440)
|
||||
readonly property int centerWidth: Config.lock.sizes.centerWidth * centerScale
|
||||
required property var lock
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
Layout.preferredWidth: centerWidth
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.hourStr
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: ":"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.clock
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * 3 * root.centerScale)
|
||||
text: Time.minuteStr
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: -Appearance.padding.large * 2
|
||||
color: DynamicColors.palette.m3tertiary
|
||||
font.bold: true
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Math.floor(Appearance.font.size.extraLarge * root.centerScale)
|
||||
text: Time.format("dddd, d MMMM yyyy")
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Appearance.spacing.large * 2
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: root.centerWidth / 2
|
||||
implicitWidth: root.centerWidth / 2
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Math.floor(root.centerWidth / 4)
|
||||
text: "person"
|
||||
}
|
||||
|
||||
CachingImage {
|
||||
id: pfp
|
||||
|
||||
anchors.fill: parent
|
||||
path: `${Paths.home}/.face`
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
focus: true
|
||||
implicitHeight: input.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: root.centerWidth * 0.8
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (root.lock.unlocking)
|
||||
return;
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
|
||||
inputField.placeholder.animate = false;
|
||||
|
||||
root.lock.pam.handleKey(event);
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus)
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
|
||||
cursorShape: Qt.IBeamCursor
|
||||
hoverEnabled: false
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: input
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.small
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Item {
|
||||
implicitHeight: fprintIcon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
|
||||
MaterialIcon {
|
||||
id: fprintIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
color: root.lock.pam.fprint.tries >= Config.lock.maxFprintTries ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
||||
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= Config.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CircularIndicator {
|
||||
anchors.fill: parent
|
||||
running: root.lock.pam.passwd.active
|
||||
}
|
||||
}
|
||||
|
||||
InputField {
|
||||
id: inputField
|
||||
|
||||
pam: root.lock.pam
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3primary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.lock.pam.passwd.start();
|
||||
}
|
||||
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: enterIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: root.lock.pam.buffer ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
font.weight: 500
|
||||
text: "arrow_forward"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.spacing.large
|
||||
implicitHeight: Math.max(message.implicitHeight, stateMessage.implicitHeight)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: stateMessage
|
||||
|
||||
readonly property string msg: {
|
||||
if (Hypr.kbLayout !== Hypr.defaultKbLayout) {
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.\nKeyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON. Kb layout: %1").arg(Hypr.kbLayoutFull);
|
||||
return qsTr("Keyboard layout: %1").arg(Hypr.kbLayoutFull);
|
||||
}
|
||||
|
||||
if (Hypr.capsLock && Hypr.numLock)
|
||||
return qsTr("Caps lock and Num lock are ON.");
|
||||
if (Hypr.capsLock)
|
||||
return qsTr("Caps lock is ON.");
|
||||
if (Hypr.numLock)
|
||||
return qsTr("Num lock is ON.");
|
||||
|
||||
return "";
|
||||
}
|
||||
property bool shouldBeVisible
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
animateProp: "opacity"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
lineHeight: 1.2
|
||||
opacity: shouldBeVisible && !message.msg ? 1 : 0
|
||||
scale: shouldBeVisible && !message.msg ? 1 : 0.7
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
} else {
|
||||
text = msg;
|
||||
}
|
||||
shouldBeVisible = true;
|
||||
} else {
|
||||
shouldBeVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: message
|
||||
|
||||
readonly property string msg: {
|
||||
if (pam.fprintState === "error")
|
||||
return qsTr("FP ERROR: %1").arg(pam.fprint.message);
|
||||
if (pam.state === "error")
|
||||
return qsTr("PW ERROR: %1").arg(pam.passwd.message);
|
||||
|
||||
if (pam.lockMessage)
|
||||
return pam.lockMessage;
|
||||
|
||||
if (pam.state === "max" && pam.fprintState === "max")
|
||||
return qsTr("Maximum password and fingerprint attempts reached.");
|
||||
if (pam.state === "max") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Maximum password attempts reached. Please use fingerprint.");
|
||||
return qsTr("Maximum password attempts reached.");
|
||||
}
|
||||
if (pam.fprintState === "max")
|
||||
return qsTr("Maximum fingerprint attempts reached. Please use password.");
|
||||
|
||||
if (pam.state === "fail") {
|
||||
if (pam.fprint.available)
|
||||
return qsTr("Incorrect password. Please try again or use fingerprint.");
|
||||
return qsTr("Incorrect password. Please try again.");
|
||||
}
|
||||
if (pam.fprintState === "fail")
|
||||
return qsTr("Fingerprint not recognized (%1/%2). Please try again or use password.").arg(pam.fprint.tries).arg(Config.lock.maxFprintTries);
|
||||
|
||||
return "";
|
||||
}
|
||||
readonly property Pam pam: root.lock.pam
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: DynamicColors.palette.m3error
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.small
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
opacity: 0
|
||||
scale: 0.7
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
onMsgChanged: {
|
||||
if (msg) {
|
||||
if (opacity > 0) {
|
||||
animate = true;
|
||||
text = msg;
|
||||
animate = false;
|
||||
|
||||
exitAnim.stop();
|
||||
if (scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
} else {
|
||||
text = msg;
|
||||
exitAnim.stop();
|
||||
appearAnim.restart();
|
||||
}
|
||||
} else {
|
||||
appearAnim.stop();
|
||||
flashAnim.stop();
|
||||
exitAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onFlashMsg(): void {
|
||||
exitAnim.stop();
|
||||
if (message.scale < 1)
|
||||
appearAnim.restart();
|
||||
else
|
||||
flashAnim.restart();
|
||||
}
|
||||
|
||||
target: root.lock.pam
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: appearAnim
|
||||
|
||||
properties: "scale,opacity"
|
||||
target: message
|
||||
to: 1
|
||||
|
||||
onFinished: flashAnim.restart()
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: flashAnim
|
||||
|
||||
loops: 2
|
||||
|
||||
FlashAnim {
|
||||
to: 0.3
|
||||
}
|
||||
|
||||
FlashAnim {
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: exitAnim
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "scale"
|
||||
target: message
|
||||
to: 0.7
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "opacity"
|
||||
target: message
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component FlashAnim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
easing.type: Easing.Linear
|
||||
property: "opacity"
|
||||
target: message
|
||||
}
|
||||
}
|
||||
|
||||
+57
-59
@@ -5,78 +5,76 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
spacing: Appearance.spacing.large * 2
|
||||
spacing: Appearance.spacing.large * 2
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: weather.implicitHeight
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: weather.implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
topLeftRadius: Appearance.rounding.large
|
||||
|
||||
topLeftRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
WeatherInfo {
|
||||
id: weather
|
||||
|
||||
WeatherInfo {
|
||||
id: weather
|
||||
|
||||
rootHeight: root.height
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: resources.implicitHeight
|
||||
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
rootHeight: root.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: resources.implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
bottomLeftRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
Resources {
|
||||
id: resources
|
||||
|
||||
Media {
|
||||
id: media
|
||||
}
|
||||
}
|
||||
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomClippingRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
bottomLeftRadius: Appearance.rounding.large
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Center {
|
||||
lock: root.lock
|
||||
}
|
||||
Media {
|
||||
id: media
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topRightRadius: Appearance.rounding.large
|
||||
bottomRightRadius: Appearance.rounding.large
|
||||
radius: Appearance.rounding.small
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
Center {
|
||||
lock: root.lock
|
||||
}
|
||||
|
||||
NotifDock {
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
CustomRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
bottomRightRadius: Appearance.rounding.large
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.small
|
||||
topRightRadius: Appearance.rounding.large
|
||||
|
||||
NotifDock {
|
||||
lock: root.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
-123
@@ -9,156 +9,153 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
RowLayout {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
spacing: Appearance.spacing.normal
|
||||
CustomRect {
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitHeight: prompt.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: prompt.implicitWidth + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: prompt.implicitWidth + Appearance.padding.normal * 2
|
||||
implicitHeight: prompt.implicitHeight + Appearance.padding.normal * 2
|
||||
MonoText {
|
||||
id: prompt
|
||||
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: Appearance.rounding.small
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
text: ">"
|
||||
}
|
||||
}
|
||||
|
||||
MonoText {
|
||||
id: prompt
|
||||
MonoText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
text: "caelestiafetch.sh"
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: ">"
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
Layout.fillHeight: true
|
||||
active: !iconLoader.active
|
||||
|
||||
MonoText {
|
||||
Layout.fillWidth: true
|
||||
text: "caelestiafetch.sh"
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
sourceComponent: OsLogo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillHeight: true
|
||||
active: !iconLoader.active
|
||||
RowLayout {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
spacing: height * 0.15
|
||||
|
||||
sourceComponent: OsLogo {}
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
id: iconLoader
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
spacing: height * 0.15
|
||||
Layout.fillHeight: true
|
||||
active: root.width > 320
|
||||
|
||||
WrappedLoader {
|
||||
id: iconLoader
|
||||
sourceComponent: OsLogo {
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillHeight: true
|
||||
active: root.width > 320
|
||||
ColumnLayout {
|
||||
Layout.bottomMargin: Appearance.padding.normal
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: iconLoader.active ? 0 : width * 0.1
|
||||
Layout.topMargin: Appearance.padding.normal
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
sourceComponent: OsLogo {}
|
||||
}
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active && root.height > 200
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Appearance.padding.normal
|
||||
Layout.bottomMargin: Appearance.padding.normal
|
||||
Layout.leftMargin: iconLoader.active ? 0 : width * 0.1
|
||||
spacing: Appearance.spacing.normal
|
||||
sourceComponent: FetchText {
|
||||
text: `OS : ${SystemInfo.osPrettyName || SysInfo.osName}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active && root.height > 200
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: root.height > (batLoader.active ? 200 : 110)
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `OS : ${SystemInfo.osPrettyName || SysInfo.osName}`
|
||||
}
|
||||
}
|
||||
sourceComponent: FetchText {
|
||||
text: `WM : ${SystemInfo.wm}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: root.height > (batLoader.active ? 200 : 110)
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active || root.height > 110
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `WM : ${SystemInfo.wm}`
|
||||
}
|
||||
}
|
||||
sourceComponent: FetchText {
|
||||
text: `USER: ${SystemInfo.user}`
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
active: !batLoader.active || root.height > 110
|
||||
FetchText {
|
||||
text: `UP : ${SystemInfo.uptime}`
|
||||
}
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `USER: ${SystemInfo.user}`
|
||||
}
|
||||
}
|
||||
WrappedLoader {
|
||||
id: batLoader
|
||||
|
||||
FetchText {
|
||||
text: `UP : ${SystemInfo.uptime}`
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
active: UPower.displayDevice.isLaptopBattery
|
||||
|
||||
WrappedLoader {
|
||||
id: batLoader
|
||||
sourceComponent: FetchText {
|
||||
text: `BATT: ${[UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state) ? "(+) " : ""}${Math.round(UPower.displayDevice.percentage * 100)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
active: UPower.displayDevice.isLaptopBattery
|
||||
WrappedLoader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: root.height > 180
|
||||
|
||||
sourceComponent: FetchText {
|
||||
text: `BATT: ${[UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state) ? "(+) " : ""}${Math.round(UPower.displayDevice.percentage * 100)}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
WrappedLoader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: root.height > 180
|
||||
Repeater {
|
||||
model: Math.max(0, Math.min(8, root.width / (Appearance.font.size.larger * 2 + Appearance.spacing.large)))
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
CustomRect {
|
||||
required property int index
|
||||
|
||||
Repeater {
|
||||
model: Math.max(0, Math.min(8, root.width / (Appearance.font.size.larger * 2 + Appearance.spacing.large)))
|
||||
color: DynamicColors.palette[`term${index}`]
|
||||
implicitHeight: Appearance.font.size.larger * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
required property int index
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: Appearance.font.size.larger * 2
|
||||
color: DynamicColors.palette[`term${index}`]
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
visible: active
|
||||
}
|
||||
|
||||
component OsLogo: ColoredIcon {
|
||||
source: SystemInfo.osLogo
|
||||
implicitSize: height
|
||||
color: DynamicColors.palette.m3primary
|
||||
layer.enabled: Config.lock.recolorLogo || SystemInfo.isDefaultLogo
|
||||
}
|
||||
|
||||
component FetchText: MonoText {
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
component MonoText: CustomText {
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
component FetchText: MonoText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: root.width > 400 ? Appearance.font.size.larger : Appearance.font.size.normal
|
||||
}
|
||||
component MonoText: CustomText {
|
||||
font.family: Appearance.font.family.mono
|
||||
}
|
||||
component OsLogo: ColoredIcon {
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitSize: height
|
||||
layer.enabled: Config.lock.recolorLogo || SystemInfo.isDefaultLogo
|
||||
source: SystemInfo.osLogo
|
||||
}
|
||||
component WrappedLoader: Loader {
|
||||
visible: active
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
required property Lock lock
|
||||
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
|
||||
|
||||
function handleIdleAction( action: var ): void {
|
||||
if ( !action )
|
||||
return;
|
||||
|
||||
if ( action === "lock" )
|
||||
lock.lock.locked = true;
|
||||
else if ( action === "unlock" )
|
||||
lock.lock.locked = false;
|
||||
else if ( typeof action === "string" )
|
||||
Hypr.dispatch( action );
|
||||
else
|
||||
Quickshell.execDetached( action );
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Config.general.idle.timeouts
|
||||
|
||||
IdleMonitor {
|
||||
required property var modelData
|
||||
|
||||
enabled: root.enabled && modelData.timeout > 0 ? true : false
|
||||
timeout: modelData.timeout
|
||||
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
required property Lock lock
|
||||
readonly property bool enabled: !Players.list.some( p => p.isPlaying )
|
||||
|
||||
function handleIdleAction( action: var ): void {
|
||||
if ( !action )
|
||||
return;
|
||||
|
||||
if ( action === "lock" )
|
||||
lock.lock.locked = true;
|
||||
else if ( action === "unlock" )
|
||||
lock.lock.locked = false;
|
||||
else if ( typeof action === "string" )
|
||||
Hypr.dispatch( action );
|
||||
else
|
||||
Quickshell.execDetached( action );
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Config.general.idle.timeouts
|
||||
|
||||
IdleMonitor {
|
||||
required property var modelData
|
||||
|
||||
enabled: root.enabled && modelData.timeout > 0 ? true : false
|
||||
timeout: modelData.timeout
|
||||
onIsIdleChanged: root.handleIdleAction( isIdle ? modelData.idleAction : modelData.activeAction )
|
||||
}
|
||||
}
|
||||
}
|
||||
+112
-116
@@ -9,142 +9,138 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Pam pam
|
||||
readonly property alias placeholder: placeholder
|
||||
property string buffer
|
||||
property string buffer
|
||||
required property Pam pam
|
||||
readonly property alias placeholder: placeholder
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
|
||||
clip: true
|
||||
Connections {
|
||||
function onBufferChanged(): void {
|
||||
if (root.pam.buffer.length > root.buffer.length) {
|
||||
charList.bindImWidth();
|
||||
} else if (root.pam.buffer.length === 0) {
|
||||
charList.implicitWidth = charList.implicitWidth;
|
||||
placeholder.animate = true;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.pam
|
||||
root.buffer = root.pam.buffer;
|
||||
}
|
||||
|
||||
function onBufferChanged(): void {
|
||||
if (root.pam.buffer.length > root.buffer.length) {
|
||||
charList.bindImWidth();
|
||||
} else if (root.pam.buffer.length === 0) {
|
||||
charList.implicitWidth = charList.implicitWidth;
|
||||
placeholder.animate = true;
|
||||
}
|
||||
target: root.pam
|
||||
}
|
||||
|
||||
root.buffer = root.pam.buffer;
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
id: placeholder
|
||||
|
||||
CustomText {
|
||||
id: placeholder
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
opacity: root.buffer ? 0 : 1
|
||||
text: {
|
||||
if (root.pam.passwd.active)
|
||||
return qsTr("Loading...");
|
||||
if (root.pam.state === "max")
|
||||
return qsTr("You have reached the maximum number of tries");
|
||||
return qsTr("Enter your password");
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: {
|
||||
if (root.pam.passwd.active)
|
||||
return qsTr("Loading...");
|
||||
if (root.pam.state === "max")
|
||||
return qsTr("You have reached the maximum number of tries");
|
||||
return qsTr("Enter your password");
|
||||
}
|
||||
ListView {
|
||||
id: charList
|
||||
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
font.family: Appearance.font.family.mono
|
||||
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
|
||||
|
||||
opacity: root.buffer ? 0 : 1
|
||||
function bindImWidth(): void {
|
||||
imWidthBehavior.enabled = false;
|
||||
implicitWidth = Qt.binding(() => fullWidth);
|
||||
imWidthBehavior.enabled = true;
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
|
||||
implicitHeight: Appearance.font.size.normal
|
||||
implicitWidth: fullWidth
|
||||
interactive: false
|
||||
orientation: Qt.Horizontal
|
||||
spacing: Appearance.spacing.small / 2
|
||||
|
||||
ListView {
|
||||
id: charList
|
||||
delegate: CustomRect {
|
||||
id: ch
|
||||
|
||||
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
implicitHeight: charList.implicitHeight
|
||||
implicitWidth: implicitHeight
|
||||
opacity: 0
|
||||
radius: Appearance.rounding.small / 2
|
||||
scale: 0
|
||||
|
||||
function bindImWidth(): void {
|
||||
imWidthBehavior.enabled = false;
|
||||
implicitWidth = Qt.binding(() => fullWidth);
|
||||
imWidthBehavior.enabled = true;
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: implicitWidth > root.width ? -(implicitWidth - root.width) / 2 : 0
|
||||
Component.onCompleted: {
|
||||
opacity = 1;
|
||||
scale = 1;
|
||||
}
|
||||
ListView.onRemove: removeAnim.start()
|
||||
|
||||
implicitWidth: fullWidth
|
||||
implicitHeight: Appearance.font.size.normal
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
orientation: Qt.Horizontal
|
||||
spacing: Appearance.spacing.small / 2
|
||||
interactive: false
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: ch
|
||||
value: true
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.buffer.split("")
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: ch
|
||||
to: 0
|
||||
}
|
||||
|
||||
delegate: CustomRect {
|
||||
id: ch
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: ch
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: charList.implicitHeight
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: ch
|
||||
value: false
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
id: imWidthBehavior
|
||||
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
radius: Appearance.rounding.small / 2
|
||||
|
||||
opacity: 0
|
||||
scale: 0
|
||||
Component.onCompleted: {
|
||||
opacity = 1;
|
||||
scale = 1;
|
||||
}
|
||||
ListView.onRemove: removeAnim.start()
|
||||
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
PropertyAction {
|
||||
target: ch
|
||||
property: "ListView.delayRemove"
|
||||
value: true
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: ch
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: ch
|
||||
property: "scale"
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: ch
|
||||
property: "ListView.delayRemove"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: imWidthBehavior
|
||||
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: root.buffer.split("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ Scope {
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
|
||||
signal unlock
|
||||
signal requestLock
|
||||
signal unlock
|
||||
|
||||
LockSurface {
|
||||
id: lockSurface
|
||||
|
||||
lock: lock
|
||||
pam: pam
|
||||
}
|
||||
@@ -35,16 +36,17 @@ Scope {
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function lock() {
|
||||
return lock.locked = true;
|
||||
}
|
||||
|
||||
target: "lock"
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "lock"
|
||||
description: "Lock the current session"
|
||||
name: "lock"
|
||||
|
||||
onPressed: {
|
||||
lock.locked = true;
|
||||
}
|
||||
|
||||
+165
-154
@@ -8,185 +8,196 @@ import qs.Helpers
|
||||
import qs.Components
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property WlSessionLock lock
|
||||
required property Pam pam
|
||||
required property WlSessionLock lock
|
||||
required property Pam pam
|
||||
readonly property alias unlocking: unlockAnim.running
|
||||
|
||||
readonly property alias unlocking: unlockAnim.running
|
||||
color: "transparent"
|
||||
|
||||
color: "transparent"
|
||||
Connections {
|
||||
function onUnlock(): void {
|
||||
unlockAnim.start();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.lock
|
||||
target: root.lock
|
||||
}
|
||||
|
||||
function onUnlock(): void {
|
||||
unlockAnim.start();
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: unlockAnim
|
||||
|
||||
SequentialAnimation {
|
||||
id: unlockAnim
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
target: lockContent
|
||||
to: lockContent.size
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
to: lockContent.size
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: lockContent.radius
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: root.lock
|
||||
property: "locked"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "radius"
|
||||
target: lockBg
|
||||
to: lockContent.radius
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: initAnim
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "scale"
|
||||
target: content
|
||||
to: 0
|
||||
}
|
||||
|
||||
running: true
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
property: "opacity"
|
||||
target: content
|
||||
to: 0
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: Appearance.rounding.large * 1.5
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitWidth"
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitHeight"
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
property: "opacity"
|
||||
target: lockIcon
|
||||
to: 1
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: lockContent
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "locked"
|
||||
target: root.lock
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: initAnim
|
||||
|
||||
running: true
|
||||
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
property: "scale"
|
||||
target: lockContent
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: lockIcon
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: content
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "scale"
|
||||
target: content
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "radius"
|
||||
target: lockBg
|
||||
to: Appearance.rounding.large * 1.5
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "implicitWidth"
|
||||
target: lockContent
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "implicitHeight"
|
||||
target: lockContent
|
||||
to: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: background
|
||||
|
||||
anchors.fill: parent
|
||||
source: WallpaperPath.lockscreenBg
|
||||
}
|
||||
|
||||
Item {
|
||||
id: lockContent
|
||||
Item {
|
||||
id: lockContent
|
||||
|
||||
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
||||
readonly property int radius: size / 4 * Appearance.rounding.scale
|
||||
readonly property int radius: size / 4 * Appearance.rounding.scale
|
||||
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
||||
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
anchors.centerIn: parent
|
||||
implicitHeight: size
|
||||
implicitWidth: size
|
||||
scale: 0
|
||||
|
||||
scale: 0
|
||||
CustomRect {
|
||||
id: lockBg
|
||||
|
||||
CustomRect {
|
||||
id: lockBg
|
||||
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3surface
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3surface
|
||||
layer.enabled: true
|
||||
opacity: DynamicColors.transparency.enabled ? DynamicColors.transparency.base : 1
|
||||
radius: lockContent.radius
|
||||
opacity: DynamicColors.transparency.enabled ? DynamicColors.transparency.base : 1
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
|
||||
}
|
||||
}
|
||||
layer.effect: MultiEffect {
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(DynamicColors.palette.m3shadow, 0.7)
|
||||
shadowEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: lockIcon
|
||||
MaterialIcon {
|
||||
id: lockIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "lock"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 4
|
||||
font.bold: true
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
font.bold: true
|
||||
font.pointSize: Appearance.font.size.extraLarge * 4
|
||||
text: "lock"
|
||||
}
|
||||
|
||||
Content {
|
||||
id: content
|
||||
Content {
|
||||
id: content
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
|
||||
height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
|
||||
|
||||
lock: root
|
||||
opacity: 0
|
||||
scale: 0
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
height: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult - Appearance.padding.large * 2
|
||||
lock: root
|
||||
opacity: 0
|
||||
scale: 0
|
||||
width: (root.screen?.height ?? 0) * Config.lock.sizes.heightMult * Config.lock.sizes.ratio - Appearance.padding.large * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+156
-159
@@ -8,198 +8,195 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
layer.enabled: true
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
source: Players.active?.trackArtUrl ?? ""
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: mask
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: mask
|
||||
}
|
||||
Rectangle {
|
||||
id: mask
|
||||
|
||||
opacity: status === Image.Ready ? 1 : 0
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
|
||||
Rectangle {
|
||||
id: mask
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
position: 0
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
position: 0.4
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Horizontal
|
||||
GradientStop {
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
position: 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.4
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.8
|
||||
color: Qt.rgba(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
CustomText {
|
||||
Layout.bottomMargin: Appearance.spacing.larger
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
text: qsTr("Now playing")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
Layout.bottomMargin: Appearance.spacing.larger
|
||||
text: qsTr("Now playing")
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 600
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Players.active?.trackArtist ?? qsTr("No media")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
text: Players.active?.trackArtist ?? qsTr("No media")
|
||||
color: DynamicColors.palette.m3primary
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 600
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Players.active?.trackTitle ?? qsTr("No media")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
text: Players.active?.trackTitle ?? qsTr("No media")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.family: Appearance.font.family.mono
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.spacing.large * 1.2
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Appearance.spacing.large * 1.2
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoPrevious)
|
||||
Players.active.previous();
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
icon: "skip_previous"
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
icon: "skip_previous"
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canTogglePlaying)
|
||||
Players.active.togglePlaying();
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoPrevious)
|
||||
Players.active.previous();
|
||||
}
|
||||
}
|
||||
active: Players.active?.isPlaying ?? false
|
||||
animate: true
|
||||
colour: "Primary"
|
||||
icon: active ? "pause" : "play_arrow"
|
||||
level: active ? 2 : 1
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
animate: true
|
||||
icon: active ? "pause" : "play_arrow"
|
||||
colour: "Primary"
|
||||
level: active ? 2 : 1
|
||||
active: Players.active?.isPlaying ?? false
|
||||
PlayerControl {
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoNext)
|
||||
Players.active.next();
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canTogglePlaying)
|
||||
Players.active.togglePlaying();
|
||||
}
|
||||
}
|
||||
icon: "skip_next"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerControl {
|
||||
icon: "skip_next"
|
||||
component PlayerControl: CustomRect {
|
||||
id: control
|
||||
|
||||
function onClicked(): void {
|
||||
if (Players.active?.canGoNext)
|
||||
Players.active.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
property bool active
|
||||
property alias animate: controlIcon.animate
|
||||
property string colour: "Secondary"
|
||||
property alias icon: controlIcon.text
|
||||
property int level: 1
|
||||
|
||||
component PlayerControl: CustomRect {
|
||||
id: control
|
||||
function onClicked(): void {
|
||||
}
|
||||
|
||||
property alias animate: controlIcon.animate
|
||||
property alias icon: controlIcon.text
|
||||
property bool active
|
||||
property string colour: "Secondary"
|
||||
property int level: 1
|
||||
Layout.preferredWidth: implicitWidth + (controlState.pressed ? Appearance.padding.normal * 2 : active ? Appearance.padding.small * 2 : 0)
|
||||
color: active ? DynamicColors.palette[`m3${colour.toLowerCase()}`] : DynamicColors.palette[`m3${colour.toLowerCase()}Container`]
|
||||
implicitHeight: controlIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: controlIcon.implicitWidth + Appearance.padding.large * 2
|
||||
radius: active || controlState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
}
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Layout.preferredWidth: implicitWidth + (controlState.pressed ? Appearance.padding.normal * 2 : active ? Appearance.padding.small * 2 : 0)
|
||||
implicitWidth: controlIcon.implicitWidth + Appearance.padding.large * 2
|
||||
implicitHeight: controlIcon.implicitHeight + Appearance.padding.normal * 2
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
level: controlState.containsMouse && !controlState.pressed ? control.level + 1 : control.level
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
|
||||
color: active ? DynamicColors.palette[`m3${colour.toLowerCase()}`] : DynamicColors.palette[`m3${colour.toLowerCase()}Container`]
|
||||
radius: active || controlState.pressed ? Appearance.rounding.small : Appearance.rounding.normal
|
||||
StateLayer {
|
||||
id: controlState
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
level: controlState.containsMouse && !controlState.pressed ? control.level + 1 : control.level
|
||||
}
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: controlState
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
}
|
||||
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
MaterialIcon {
|
||||
id: controlIcon
|
||||
|
||||
function onClicked(): void {
|
||||
control.onClicked();
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
fill: control.active ? 1 : 0
|
||||
font.pointSize: Appearance.font.size.large
|
||||
|
||||
MaterialIcon {
|
||||
id: controlIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: control.active ? DynamicColors.palette[`m3on${control.colour}`] : DynamicColors.palette[`m3on${control.colour}Container`]
|
||||
font.pointSize: Appearance.font.size.large
|
||||
fill: control.active ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveFastSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+111
-115
@@ -11,135 +11,131 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var lock
|
||||
required property var lock
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
spacing: Appearance.spacing.smaller
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Loader {
|
||||
active: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
opacity: NotifServer.list.length > 0 ? 0 : 1
|
||||
|
||||
radius: Appearance.rounding.small
|
||||
color: "transparent"
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: opacity > 0
|
||||
opacity: NotifServer.list.length > 0 ? 0 : 1
|
||||
Image {
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
layer.enabled: true
|
||||
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
|
||||
sourceSize.width: clipRect.width * 0.8
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
layer.effect: Coloriser {
|
||||
brightness: 1
|
||||
colorizationColor: DynamicColors.palette.m3outlineVariant
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
asynchronous: true
|
||||
source: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/dino.png`)
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: clipRect.width * 0.8
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.family: Appearance.font.family.mono
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
text: qsTr("No Notifications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: Coloriser {
|
||||
colorizationColor: DynamicColors.palette.m3outlineVariant
|
||||
brightness: 1
|
||||
}
|
||||
}
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("No Notifications")
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.family: Appearance.font.family.mono
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
add: Transition {
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.extraLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
from: 0
|
||||
property: "scale"
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
delegate: NotifGroup {
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
|
||||
CustomListView {
|
||||
anchors.fill: parent
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const list = NotifServer.notClosed.map(n => [n.appName, null]);
|
||||
return [...new Map(list).keys()];
|
||||
}
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
clip: true
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const list = NotifServer.notClosed.map(n => [n.appName, null]);
|
||||
return [...new Map(list).keys()];
|
||||
}
|
||||
}
|
||||
|
||||
delegate: NotifGroup {}
|
||||
|
||||
add: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
remove: Transition {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
properties: "opacity,scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
property: "y"
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "scale"
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+247
-248
@@ -12,305 +12,304 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property string modelData
|
||||
readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? ""
|
||||
property bool expanded
|
||||
readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? ""
|
||||
required property string modelData
|
||||
readonly property list<var> notifs: NotifServer.list.filter(notif => notif.appName === modelData)
|
||||
readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low"
|
||||
|
||||
readonly property list<var> notifs: NotifServer.list.filter(notif => notif.appName === modelData)
|
||||
readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? ""
|
||||
readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? ""
|
||||
readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low"
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
clip: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
property bool expanded
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
clip: true
|
||||
radius: Appearance.rounding.normal
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Appearance.padding.normal
|
||||
Component {
|
||||
id: imageComp
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
Image {
|
||||
asynchronous: true
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
height: Config.notifs.sizes.image
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
width: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
Component {
|
||||
id: appIconComp
|
||||
|
||||
Component {
|
||||
id: imageComp
|
||||
ColoredIcon {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
cache: false
|
||||
asynchronous: true
|
||||
width: Config.notifs.sizes.image
|
||||
height: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: materialIconComp
|
||||
|
||||
Component {
|
||||
id: appIconComp
|
||||
MaterialIcon {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
}
|
||||
}
|
||||
|
||||
ColoredIcon {
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
ClippingRectangle {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Component {
|
||||
id: materialIconComp
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: root.appIcon && root.image
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
ClippingRectangle {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
sourceComponent: CustomRect {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.palette.m3surfaceContainerHighest : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
ColoredIcon {
|
||||
anchors.centerIn: parent
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
active: root.appIcon && root.image
|
||||
ColumnLayout {
|
||||
Layout.bottomMargin: -Appearance.padding.small / 2 - (root.expanded ? 0 : spacing)
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -Appearance.padding.small
|
||||
spacing: Math.round(Appearance.spacing.small / 2)
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
RowLayout {
|
||||
Layout.bottomMargin: -parent.spacing
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.smaller
|
||||
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : root.urgency === "low" ? DynamicColors.palette.m3surfaceContainerHighest : DynamicColors.palette.m3secondaryContainer
|
||||
radius: Appearance.rounding.full
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.modelData
|
||||
}
|
||||
|
||||
ColoredIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : root.urgency === "low" ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.notifs[0]?.timeStr ?? ""
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: -Appearance.padding.small
|
||||
Layout.bottomMargin: -Appearance.padding.small / 2 - (root.expanded ? 0 : spacing)
|
||||
Layout.fillWidth: true
|
||||
spacing: Math.round(Appearance.spacing.small / 2)
|
||||
CustomRect {
|
||||
Layout.preferredWidth: root.notifs.length > Config.notifs.groupPreviewNum ? implicitWidth : 0
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
implicitHeight: groupCount.implicitHeight + Appearance.padding.small
|
||||
implicitWidth: expandBtn.implicitWidth + Appearance.padding.smaller * 2
|
||||
opacity: root.notifs.length > Config.notifs.groupPreviewNum ? 1 : 0
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: -parent.spacing
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.smaller
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: root.modelData
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.small
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
|
||||
CustomText {
|
||||
animate: true
|
||||
text: root.notifs[0]?.timeStr ?? ""
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: expandBtn.implicitWidth + Appearance.padding.smaller * 2
|
||||
implicitHeight: groupCount.implicitHeight + Appearance.padding.small
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
radius: Appearance.rounding.full
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small / 2
|
||||
|
||||
opacity: root.notifs.length > Config.notifs.groupPreviewNum ? 1 : 0
|
||||
Layout.preferredWidth: root.notifs.length > Config.notifs.groupPreviewNum ? implicitWidth : 0
|
||||
CustomText {
|
||||
id: groupCount
|
||||
|
||||
StateLayer {
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
Layout.leftMargin: Appearance.padding.small / 2
|
||||
animate: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: Appearance.font.size.small
|
||||
text: root.notifs.length
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -Appearance.padding.small / 2
|
||||
animate: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(0, Config.notifs.groupPreviewNum)
|
||||
}
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.small / 2
|
||||
NotifLine {
|
||||
id: notif
|
||||
|
||||
CustomText {
|
||||
id: groupCount
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
|
||||
Layout.leftMargin: Appearance.padding.small / 2
|
||||
animate: true
|
||||
text: root.notifs.length
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: Appearance.font.size.small
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -Appearance.padding.small / 2
|
||||
animate: true
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
from: 0.7
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
Anim {
|
||||
from: 0
|
||||
property: "preferredHeight"
|
||||
target: notif.Layout
|
||||
to: notif.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(0, Config.notifs.groupPreviewNum)
|
||||
}
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
|
||||
NotifLine {
|
||||
id: notif
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 0
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 0.7
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
from: 0.7
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif.Layout
|
||||
property: "preferredHeight"
|
||||
from: 0
|
||||
to: notif.implicitHeight
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
property: "preferredHeight"
|
||||
target: notif.Layout
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.expanded ? implicitHeight : 0
|
||||
active: opacity > 0
|
||||
opacity: root.expanded ? 1 : 0
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
to: 0.7
|
||||
}
|
||||
Anim {
|
||||
target: notif.Layout
|
||||
property: "preferredHeight"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(Config.notifs.groupPreviewNum)
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
NotifLine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.expanded ? 1 : 0
|
||||
Layout.preferredHeight: root.expanded ? implicitHeight : 0
|
||||
active: opacity > 0
|
||||
component NotifLine: CustomText {
|
||||
id: notifLine
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.notifs.slice(Config.notifs.groupPreviewNum)
|
||||
}
|
||||
required property NotifServer.Notif modelData
|
||||
|
||||
NotifLine {}
|
||||
}
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
text: {
|
||||
const summary = modelData.summary.replace(/\n/g, " ");
|
||||
const body = modelData.body.replace(/\n/g, " ");
|
||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (metrics.text === metrics.elidedText)
|
||||
return `${summary} <span style='color:${color}'>${body}</span>`;
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
const t = metrics.elidedText.length - 3;
|
||||
if (t < summary.length)
|
||||
return `${summary.slice(0, t)}...`;
|
||||
|
||||
component NotifLine: CustomText {
|
||||
id: notifLine
|
||||
return `${summary} <span style='color:${color}'>${body.slice(0, t - summary.length)}...</span>`;
|
||||
}
|
||||
textFormat: Text.MarkdownText
|
||||
|
||||
required property NotifServer.Notif modelData
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
Layout.fillWidth: true
|
||||
textFormat: Text.MarkdownText
|
||||
text: {
|
||||
const summary = modelData.summary.replace(/\n/g, " ");
|
||||
const body = modelData.body.replace(/\n/g, " ");
|
||||
const color = root.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline;
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
if (metrics.text === metrics.elidedText)
|
||||
return `${summary} <span style='color:${color}'>${body}</span>`;
|
||||
|
||||
const t = metrics.elidedText.length - 3;
|
||||
if (t < summary.length)
|
||||
return `${summary.slice(0, t)}...`;
|
||||
|
||||
return `${summary} <span style='color:${color}'>${body.slice(0, t - summary.length)}...</span>`;
|
||||
}
|
||||
color: root.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
|
||||
text: `${notifLine.modelData.summary} ${notifLine.modelData.body}`.replace(/\n/g, " ")
|
||||
font.pointSize: notifLine.font.pointSize
|
||||
font.family: notifLine.font.family
|
||||
elideWidth: notifLine.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
elide: Text.ElideRight
|
||||
elideWidth: notifLine.width
|
||||
font.family: notifLine.font.family
|
||||
font.pointSize: notifLine.font.pointSize
|
||||
text: `${notifLine.modelData.summary} ${notifLine.modelData.body}`.replace(/\n/g, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+150
-149
@@ -6,188 +6,189 @@ import QtQuick
|
||||
import qs.Config
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property WlSessionLock lock
|
||||
property string buffer
|
||||
readonly property alias fprint: fprint
|
||||
property string fprintState
|
||||
required property WlSessionLock lock
|
||||
property string lockMessage
|
||||
readonly property alias passwd: passwd
|
||||
property string state
|
||||
|
||||
readonly property alias passwd: passwd
|
||||
readonly property alias fprint: fprint
|
||||
property string lockMessage
|
||||
property string state
|
||||
property string fprintState
|
||||
property string buffer
|
||||
signal flashMsg
|
||||
|
||||
signal flashMsg
|
||||
function handleKey(event: KeyEvent): void {
|
||||
if (passwd.active || state === "max")
|
||||
return;
|
||||
|
||||
function handleKey(event: KeyEvent): void {
|
||||
if (passwd.active || state === "max")
|
||||
return;
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
||||
passwd.start();
|
||||
} else if (event.key === Qt.Key_Backspace) {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
buffer = "";
|
||||
} else {
|
||||
buffer = buffer.slice(0, -1);
|
||||
}
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
buffer += event.text;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
|
||||
passwd.start();
|
||||
} else if (event.key === Qt.Key_Backspace) {
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
buffer = "";
|
||||
} else {
|
||||
buffer = buffer.slice(0, -1);
|
||||
}
|
||||
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
|
||||
// No illegal characters (you are insane if you use unicode in your password)
|
||||
buffer += event.text;
|
||||
}
|
||||
}
|
||||
PamContext {
|
||||
id: passwd
|
||||
|
||||
PamContext {
|
||||
id: passwd
|
||||
config: "passwd"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
|
||||
config: "passwd"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
onCompleted: res => {
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked"))
|
||||
root.lockMessage = message;
|
||||
else if (root.lockMessage && message.endsWith(" left to unlock)"))
|
||||
root.lockMessage += "\n" + message;
|
||||
}
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
root.state = "max";
|
||||
else if (res === PamResult.Failed)
|
||||
root.state = "fail";
|
||||
|
||||
onResponseRequiredChanged: {
|
||||
if (!responseRequired)
|
||||
return;
|
||||
root.flashMsg();
|
||||
stateReset.restart();
|
||||
}
|
||||
onMessageChanged: {
|
||||
if (message.startsWith("The account is locked"))
|
||||
root.lockMessage = message;
|
||||
else if (root.lockMessage && message.endsWith(" left to unlock)"))
|
||||
root.lockMessage += "\n" + message;
|
||||
}
|
||||
onResponseRequiredChanged: {
|
||||
if (!responseRequired)
|
||||
return;
|
||||
|
||||
respond(root.buffer);
|
||||
root.buffer = "";
|
||||
}
|
||||
respond(root.buffer);
|
||||
root.buffer = "";
|
||||
}
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
PamContext {
|
||||
id: fprint
|
||||
|
||||
if (res === PamResult.Error)
|
||||
root.state = "error";
|
||||
else if (res === PamResult.MaxTries)
|
||||
root.state = "max";
|
||||
else if (res === PamResult.Failed)
|
||||
root.state = "fail";
|
||||
property bool available
|
||||
property int errorTries
|
||||
property int tries
|
||||
|
||||
root.flashMsg();
|
||||
stateReset.restart();
|
||||
}
|
||||
}
|
||||
function checkAvail(): void {
|
||||
if (!available || !Config.lock.enableFprint || !root.lock.secure) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
|
||||
PamContext {
|
||||
id: fprint
|
||||
tries = 0;
|
||||
errorTries = 0;
|
||||
start();
|
||||
}
|
||||
|
||||
property bool available
|
||||
property int tries
|
||||
property int errorTries
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
|
||||
function checkAvail(): void {
|
||||
if (!available || !Config.lock.enableFprint || !root.lock.secure) {
|
||||
abort();
|
||||
return;
|
||||
}
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
return;
|
||||
|
||||
tries = 0;
|
||||
errorTries = 0;
|
||||
start();
|
||||
}
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
|
||||
config: "fprint"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
if (res === PamResult.Error) {
|
||||
root.fprintState = "error";
|
||||
errorTries++;
|
||||
if (errorTries < 5) {
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
}
|
||||
} else if (res === PamResult.MaxTries) {
|
||||
// Isn't actually the real max tries as pam only reports completed
|
||||
// when max tries is reached.
|
||||
tries++;
|
||||
if (tries < Config.lock.maxFprintTries) {
|
||||
// Restart if not actually real max tries
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
onCompleted: res => {
|
||||
if (!available)
|
||||
return;
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (res === PamResult.Success)
|
||||
return root.lock.unlock();
|
||||
Process {
|
||||
id: availProc
|
||||
|
||||
if (res === PamResult.Error) {
|
||||
root.fprintState = "error";
|
||||
errorTries++;
|
||||
if (errorTries < 5) {
|
||||
abort();
|
||||
errorRetry.restart();
|
||||
}
|
||||
} else if (res === PamResult.MaxTries) {
|
||||
// Isn't actually the real max tries as pam only reports completed
|
||||
// when max tries is reached.
|
||||
tries++;
|
||||
if (tries < Config.lock.maxFprintTries) {
|
||||
// Restart if not actually real max tries
|
||||
root.fprintState = "fail";
|
||||
start();
|
||||
} else {
|
||||
root.fprintState = "max";
|
||||
abort();
|
||||
}
|
||||
}
|
||||
command: ["sh", "-c", "fprintd-list $USER"]
|
||||
|
||||
root.flashMsg();
|
||||
fprintStateReset.start();
|
||||
}
|
||||
}
|
||||
onExited: code => {
|
||||
fprint.available = code === 0;
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: availProc
|
||||
Timer {
|
||||
id: errorRetry
|
||||
|
||||
command: ["sh", "-c", "fprintd-list $USER"]
|
||||
onExited: code => {
|
||||
fprint.available = code === 0;
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
interval: 800
|
||||
|
||||
Timer {
|
||||
id: errorRetry
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
|
||||
interval: 800
|
||||
onTriggered: fprint.start()
|
||||
}
|
||||
Timer {
|
||||
id: stateReset
|
||||
|
||||
Timer {
|
||||
id: stateReset
|
||||
interval: 4000
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
if (root.state !== "max")
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
onTriggered: {
|
||||
if (root.state !== "max")
|
||||
root.state = "";
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: fprintStateReset
|
||||
Timer {
|
||||
id: fprintStateReset
|
||||
|
||||
interval: 4000
|
||||
onTriggered: {
|
||||
root.fprintState = "";
|
||||
fprint.errorTries = 0;
|
||||
}
|
||||
}
|
||||
interval: 4000
|
||||
|
||||
Connections {
|
||||
target: root.lock
|
||||
onTriggered: {
|
||||
root.fprintState = "";
|
||||
fprint.errorTries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function onSecureChanged(): void {
|
||||
if (root.lock.secure) {
|
||||
availProc.running = true;
|
||||
root.buffer = "";
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onSecureChanged(): void {
|
||||
if (root.lock.secure) {
|
||||
availProc.running = true;
|
||||
root.buffer = "";
|
||||
root.state = "";
|
||||
root.fprintState = "";
|
||||
root.lockMessage = "";
|
||||
}
|
||||
}
|
||||
|
||||
function onUnlock(): void {
|
||||
fprint.abort();
|
||||
}
|
||||
}
|
||||
function onUnlock(): void {
|
||||
fprint.abort();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config.lock
|
||||
target: root.lock
|
||||
}
|
||||
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
function onEnableFprintChanged(): void {
|
||||
fprint.checkAvail();
|
||||
}
|
||||
|
||||
target: Config.lock
|
||||
}
|
||||
}
|
||||
|
||||
+57
-59
@@ -6,75 +6,73 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large
|
||||
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
|
||||
|
||||
rowSpacing: Appearance.spacing.large
|
||||
columnSpacing: Appearance.spacing.large
|
||||
rows: 1
|
||||
columns: 2
|
||||
Ref {
|
||||
service: SystemUsage
|
||||
}
|
||||
|
||||
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
|
||||
icon: "memory"
|
||||
value: SystemUsage.cpuPerc
|
||||
colour: DynamicColors.palette.m3primary
|
||||
}
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
}
|
||||
|
||||
Resource {
|
||||
Layout.bottomMargin: Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large
|
||||
icon: "memory_alt"
|
||||
value: SystemUsage.memPerc
|
||||
colour: DynamicColors.palette.m3secondary
|
||||
}
|
||||
component Resource: CustomRect {
|
||||
id: res
|
||||
|
||||
component Resource: CustomRect {
|
||||
id: res
|
||||
required property color colour
|
||||
required property string icon
|
||||
required property real value
|
||||
|
||||
required property string icon
|
||||
required property real value
|
||||
required property color colour
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: width
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: width
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Appearance.rounding.large
|
||||
CircularProgress {
|
||||
id: circ
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
value: res.value
|
||||
padding: Appearance.padding.large * 3
|
||||
fgColour: res.colour
|
||||
bgColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3)
|
||||
strokeWidth: width < 200 ? Appearance.padding.smaller : Appearance.padding.normal
|
||||
}
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: res.icon
|
||||
color: res.colour
|
||||
font.pointSize: (circ.arcRadius * 0.7) || 1
|
||||
font.weight: 600
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
color: res.colour
|
||||
font.pointSize: (circ.arcRadius * 0.7) || 1
|
||||
font.weight: 600
|
||||
text: res.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ Item {
|
||||
id: root
|
||||
|
||||
ClippingRectangle {
|
||||
radius: 1000
|
||||
anchors.fill: parent
|
||||
radius: 1000
|
||||
|
||||
Image {
|
||||
id: userImage
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
sourceSize.width: parent.width
|
||||
sourceSize.height: parent.height
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
source: `${Paths.home}/.face`
|
||||
sourceSize.height: parent.height
|
||||
sourceSize.width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+133
-139
@@ -7,169 +7,163 @@ import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property int rootHeight
|
||||
required property int rootHeight
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large * 2
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
spacing: Appearance.spacing.small
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: -Appearance.padding.large
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
active: root.rootHeight > 610
|
||||
visible: active
|
||||
|
||||
Loader {
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
Layout.bottomMargin: -Appearance.padding.large
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceComponent: CustomText {
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
text: qsTr("Weather")
|
||||
}
|
||||
}
|
||||
|
||||
active: root.rootHeight > 610
|
||||
visible: active
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
sourceComponent: CustomText {
|
||||
text: qsTr("Weather")
|
||||
color: DynamicColors.palette.m3primary
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2.5
|
||||
text: Weather.icon
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.large
|
||||
ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
text: Weather.icon
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2.5
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3secondary
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
text: Weather.description
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
text: qsTr("Humidity: %1%").arg(Weather.humidity)
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
Loader {
|
||||
Layout.rightMargin: Appearance.padding.smaller
|
||||
active: root.width > 400
|
||||
visible: active
|
||||
|
||||
animate: true
|
||||
text: Weather.description
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3primary
|
||||
elide: Text.ElideLeft
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: Weather.temp
|
||||
}
|
||||
|
||||
animate: true
|
||||
text: qsTr("Humidity: %1%").arg(Weather.humidity)
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideLeft
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.rightMargin: Appearance.padding.smaller
|
||||
active: root.width > 400
|
||||
visible: active
|
||||
Loader {
|
||||
id: forecastLoader
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Appearance.spacing.small
|
||||
Layout.bottomMargin: Appearance.padding.large * 2
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Appearance.spacing.smaller
|
||||
active: root.rootHeight > 820
|
||||
visible: active
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
animate: true
|
||||
text: Weather.temp
|
||||
color: DynamicColors.palette.m3primary
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.weight: 500
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
Repeater {
|
||||
model: {
|
||||
const forecast = Weather.hourlyForecast;
|
||||
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
|
||||
if (!forecast)
|
||||
return Array.from({
|
||||
length: count
|
||||
}, () => null);
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
return forecast.slice(0, count);
|
||||
}
|
||||
|
||||
animate: true
|
||||
text: qsTr("Feels like: %1").arg(Weather.feelsLike)
|
||||
color: DynamicColors.palette.m3outline
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: Appearance.font.size.smaller
|
||||
elide: Text.ElideLeft
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: forecastHour
|
||||
|
||||
Loader {
|
||||
id: forecastLoader
|
||||
required property var modelData
|
||||
|
||||
Layout.topMargin: Appearance.spacing.smaller
|
||||
Layout.bottomMargin: Appearance.padding.large * 2
|
||||
Layout.fillWidth: true
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
active: root.rootHeight > 820
|
||||
visible: active
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: {
|
||||
const hour = forecastHour.modelData?.hour ?? 0;
|
||||
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
spacing: Appearance.spacing.large
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.extraLarge * 1.5
|
||||
font.weight: 500
|
||||
text: forecastHour.modelData?.icon ?? "cloud_alert"
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
const forecast = Weather.hourlyForecast;
|
||||
const count = root.width < 320 ? 3 : root.width < 400 ? 4 : 5;
|
||||
if (!forecast)
|
||||
return Array.from({
|
||||
length: count
|
||||
}, () => null);
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return forecast.slice(0, count);
|
||||
}
|
||||
Timer {
|
||||
interval: 900000 // 15 minutes
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
|
||||
ColumnLayout {
|
||||
id: forecastHour
|
||||
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
const hour = forecastHour.modelData?.hour ?? 0;
|
||||
return hour > 12 ? `${(hour - 12).toString().padStart(2, "0")} PM` : `${hour.toString().padStart(2, "0")} AM`;
|
||||
}
|
||||
color: DynamicColors.palette.m3outline
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: forecastHour.modelData?.icon ?? "cloud_alert"
|
||||
font.pointSize: Appearance.font.size.extraLarge * 1.5
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Config.services.useFahrenheit ? `${forecastHour.modelData?.tempF ?? 0}°F` : `${forecastHour.modelData?.tempC ?? 0}°C`
|
||||
color: DynamicColors.palette.m3secondary
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
repeat: true
|
||||
interval: 900000 // 15 minutes
|
||||
onTriggered: Weather.reload()
|
||||
}
|
||||
onTriggered: Weather.reload()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ Item {
|
||||
|
||||
CustomRadioButton {
|
||||
id: network
|
||||
visible: modelData.name !== "lo"
|
||||
|
||||
required property NetworkDevice modelData
|
||||
|
||||
checked: Helpers.Network.activeDevice?.name === modelData.name
|
||||
text: modelData.description
|
||||
visible: modelData.name !== "lo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,19 @@ import qs.Modules
|
||||
Item {
|
||||
id: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.top: parent.top
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
anchors.top: parent.top
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
|
||||
MaterialIcon {
|
||||
text: "android_wifi_4_bar"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "android_wifi_4_bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-11
@@ -6,40 +6,42 @@ import qs.Helpers
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property Wrapper popouts
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitWidth: 25
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: 25
|
||||
|
||||
CustomRect {
|
||||
anchors.bottomMargin: 3
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 3
|
||||
anchors.bottomMargin: 3
|
||||
color: "transparent"
|
||||
radius: 4
|
||||
|
||||
MaterialIcon {
|
||||
id: notificationCenterIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
property color iconColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||
anchors.centerIn: parent
|
||||
color: iconColor
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pixelSize: 20
|
||||
color: iconColor
|
||||
text: HasNotifications.hasNotifications ? "\uf4fe" : "\ue7f4"
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
root.visibilities.sidebar = !root.visibilities.sidebar;
|
||||
}
|
||||
|
||||
@@ -4,50 +4,56 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var sidebar
|
||||
readonly property real rounding: 8
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: 8
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property var sidebar
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width + root.rounding)
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.sidebar.notifsRoundingX
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.sidebar.notifsRoundingX
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.rounding
|
||||
radiusX: root.rounding
|
||||
radiusY: root.rounding
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width + root.rounding)
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.sidebar.notifsRoundingX
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.sidebar.notifsRoundingX
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: root.rounding
|
||||
relativeX: root.rounding
|
||||
relativeY: root.rounding
|
||||
}
|
||||
}
|
||||
|
||||
+153
-155
@@ -6,198 +6,196 @@ import Quickshell.Widgets
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property Item panels
|
||||
readonly property int padding: 6
|
||||
readonly property int padding: 6
|
||||
required property Item panels
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
implicitHeight: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
implicitWidth: Config.notifs.sizes.width + padding * 2
|
||||
implicitHeight: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
let height = (count - 1) * 8;
|
||||
for (let i = 0; i < count; i++)
|
||||
height += list.itemAtIndex(i)?.nonAnimHeight ?? 0;
|
||||
|
||||
let height = (count - 1) * 8;
|
||||
for (let i = 0; i < count; i++)
|
||||
height += list.itemAtIndex(i)?.nonAnimHeight ?? 0;
|
||||
if (visibilities && panels) {
|
||||
if (visibilities.osd) {
|
||||
const h = panels.osd.y - 8 * 2 - padding * 2;
|
||||
if (height > h)
|
||||
height = h;
|
||||
}
|
||||
|
||||
if (visibilities && panels) {
|
||||
if (visibilities.osd) {
|
||||
const h = panels.osd.y - 8 * 2 - padding * 2;
|
||||
if (height > h)
|
||||
height = h;
|
||||
}
|
||||
if (visibilities.session) {
|
||||
const h = panels.session.y - 8 * 2 - padding * 2;
|
||||
if (height > h)
|
||||
height = h;
|
||||
}
|
||||
}
|
||||
|
||||
if (visibilities.session) {
|
||||
const h = panels.session.y - 8 * 2 - padding * 2;
|
||||
if (height > h)
|
||||
height = h;
|
||||
}
|
||||
}
|
||||
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
|
||||
}
|
||||
implicitWidth: Config.notifs.sizes.width + padding * 2
|
||||
|
||||
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
ClippingWrapperRectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
ClippingWrapperRectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
color: "transparent"
|
||||
radius: 4
|
||||
|
||||
color: "transparent"
|
||||
radius: 4
|
||||
CustomListView {
|
||||
id: list
|
||||
|
||||
CustomListView {
|
||||
id: list
|
||||
anchors.fill: parent
|
||||
cacheBuffer: QsWindow.window?.screen.height ?? 0
|
||||
orientation: Qt.Vertical
|
||||
spacing: 0
|
||||
|
||||
model: ScriptModel {
|
||||
values: NotifServer.popups.filter(n => !n.closed)
|
||||
}
|
||||
delegate: Item {
|
||||
id: wrapper
|
||||
|
||||
anchors.fill: parent
|
||||
property int idx
|
||||
required property int index
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property alias nonAnimHeight: notif.nonAnimHeight
|
||||
|
||||
orientation: Qt.Vertical
|
||||
spacing: 0
|
||||
cacheBuffer: QsWindow.window?.screen.height ?? 0
|
||||
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
|
||||
implicitWidth: notif.implicitWidth
|
||||
|
||||
delegate: Item {
|
||||
id: wrapper
|
||||
ListView.onRemove: removeAnim.start()
|
||||
onIndexChanged: {
|
||||
if (index !== -1)
|
||||
idx = index;
|
||||
}
|
||||
|
||||
required property NotifServer.Notif modelData
|
||||
required property int index
|
||||
readonly property alias nonAnimHeight: notif.nonAnimHeight
|
||||
property int idx
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
onIndexChanged: {
|
||||
if (index !== -1)
|
||||
idx = index;
|
||||
}
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: wrapper
|
||||
value: true
|
||||
}
|
||||
|
||||
implicitWidth: notif.implicitWidth
|
||||
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
|
||||
PropertyAction {
|
||||
property: "enabled"
|
||||
target: wrapper
|
||||
value: false
|
||||
}
|
||||
|
||||
ListView.onRemove: removeAnim.start()
|
||||
PropertyAction {
|
||||
property: "implicitHeight"
|
||||
target: wrapper
|
||||
value: 0
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
PropertyAction {
|
||||
property: "z"
|
||||
target: wrapper
|
||||
value: 1
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
target: wrapper
|
||||
property: "ListView.delayRemove"
|
||||
value: true
|
||||
}
|
||||
PropertyAction {
|
||||
target: wrapper
|
||||
property: "enabled"
|
||||
value: false
|
||||
}
|
||||
PropertyAction {
|
||||
target: wrapper
|
||||
property: "implicitHeight"
|
||||
value: 0
|
||||
}
|
||||
PropertyAction {
|
||||
target: wrapper
|
||||
property: "z"
|
||||
value: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "x"
|
||||
to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
PropertyAction {
|
||||
target: wrapper
|
||||
property: "ListView.delayRemove"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
property: "x"
|
||||
target: notif
|
||||
to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2
|
||||
}
|
||||
|
||||
ClippingRectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
|
||||
PropertyAction {
|
||||
property: "ListView.delayRemove"
|
||||
target: wrapper
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
radius: 4
|
||||
implicitWidth: notif.implicitWidth
|
||||
implicitHeight: notif.implicitHeight
|
||||
ClippingRectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
|
||||
color: "transparent"
|
||||
implicitHeight: notif.implicitHeight
|
||||
implicitWidth: notif.implicitWidth
|
||||
radius: 4
|
||||
|
||||
Notification {
|
||||
id: notif
|
||||
Notification {
|
||||
id: notif
|
||||
|
||||
modelData: wrapper.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
modelData: wrapper.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: NotifServer.popups.filter(n => !n.closed)
|
||||
}
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
|
||||
move: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
ExtraIndicator {
|
||||
anchors.top: parent.top
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
displaced: Transition {
|
||||
Anim {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
const scrollY = list.contentY;
|
||||
|
||||
ExtraIndicator {
|
||||
anchors.top: parent.top
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
let height = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
|
||||
const scrollY = list.contentY;
|
||||
if (height - 8 >= scrollY)
|
||||
return i;
|
||||
}
|
||||
|
||||
let height = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
if (height - 8 >= scrollY)
|
||||
return i;
|
||||
}
|
||||
ExtraIndicator {
|
||||
anchors.bottom: parent.bottom
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
const scrollY = list.contentHeight - (list.contentY + list.height);
|
||||
|
||||
ExtraIndicator {
|
||||
anchors.bottom: parent.bottom
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
let height = 0;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
|
||||
const scrollY = list.contentHeight - (list.contentY + list.height);
|
||||
if (height - 8 >= scrollY)
|
||||
return count - i - 1;
|
||||
}
|
||||
|
||||
let height = 0;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (height - 8 >= scrollY)
|
||||
return count - i - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
easing.type: Easing.BezierSpline
|
||||
component Anim: NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,473 +12,456 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property bool hasImage: modelData.image.length > 0
|
||||
readonly property bool hasAppIcon: modelData.appIcon.length > 0
|
||||
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
|
||||
property bool expanded: Config.notifs.openExpanded
|
||||
property bool expanded: Config.notifs.openExpanded
|
||||
readonly property bool hasAppIcon: modelData.appIcon.length > 0
|
||||
readonly property bool hasImage: modelData.image.length > 0
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
|
||||
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 6
|
||||
implicitWidth: Config.notifs.sizes.width
|
||||
implicitHeight: inner.implicitHeight
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: inner.implicitHeight
|
||||
implicitWidth: Config.notifs.sizes.width
|
||||
radius: 6
|
||||
x: Config.notifs.sizes.width
|
||||
|
||||
x: Config.notifs.sizes.width
|
||||
Component.onCompleted: {
|
||||
x = 0;
|
||||
modelData.lock(this);
|
||||
}
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
Behavior on x {
|
||||
Anim {
|
||||
Behavior on x {
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
property int startY
|
||||
Component.onCompleted: {
|
||||
x = 0;
|
||||
modelData.lock(this);
|
||||
}
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: root.expanded && body.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
|
||||
preventStealing: true
|
||||
MouseArea {
|
||||
property int startY
|
||||
|
||||
onEntered: root.modelData.timer.stop()
|
||||
onExited: {
|
||||
if (!pressed)
|
||||
root.modelData.timer.start();
|
||||
}
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
|
||||
anchors.fill: parent
|
||||
cursorShape: root.expanded && body.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
|
||||
drag.axis: Drag.XAxis
|
||||
drag.target: parent
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
|
||||
drag.target: parent
|
||||
drag.axis: Drag.XAxis
|
||||
onClicked: event => {
|
||||
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton)
|
||||
return;
|
||||
|
||||
onPressed: event => {
|
||||
root.modelData.timer.stop();
|
||||
startY = event.y;
|
||||
if (event.button === Qt.MiddleButton)
|
||||
root.modelData.close();
|
||||
}
|
||||
onReleased: event => {
|
||||
if (!containsMouse)
|
||||
root.modelData.timer.start();
|
||||
const actions = root.modelData.actions;
|
||||
if (actions?.length === 1)
|
||||
actions[0].invoke();
|
||||
}
|
||||
onEntered: root.modelData.timer.stop()
|
||||
onExited: {
|
||||
if (!pressed)
|
||||
root.modelData.timer.start();
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
root.expanded = diffY > 0;
|
||||
}
|
||||
}
|
||||
onPressed: event => {
|
||||
root.modelData.timer.stop();
|
||||
startY = event.y;
|
||||
if (event.button === Qt.MiddleButton)
|
||||
root.modelData.close();
|
||||
}
|
||||
onReleased: event => {
|
||||
if (!containsMouse)
|
||||
root.modelData.timer.start();
|
||||
|
||||
if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
|
||||
root.x = 0;
|
||||
else
|
||||
root.modelData.popup = false;
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
root.expanded = diffY > 0;
|
||||
}
|
||||
}
|
||||
onClicked: event => {
|
||||
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton)
|
||||
return;
|
||||
if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
|
||||
root.x = 0;
|
||||
else
|
||||
root.modelData.popup = false;
|
||||
}
|
||||
|
||||
const actions = root.modelData.actions;
|
||||
if (actions?.length === 1)
|
||||
actions[0].invoke();
|
||||
}
|
||||
Item {
|
||||
id: inner
|
||||
|
||||
Item {
|
||||
id: inner
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
implicitHeight: root.nonAnimHeight
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: 8
|
||||
|
||||
implicitHeight: root.nonAnimHeight
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: image
|
||||
Loader {
|
||||
id: image
|
||||
|
||||
active: root.hasImage
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: Config.notifs.sizes.image
|
||||
height: Config.notifs.sizes.image
|
||||
visible: root.hasImage || root.hasAppIcon
|
||||
active: root.hasImage
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
asynchronous: true
|
||||
height: Config.notifs.sizes.image
|
||||
visible: root.hasImage || root.hasAppIcon
|
||||
width: Config.notifs.sizes.image
|
||||
|
||||
sourceComponent: ClippingRectangle {
|
||||
radius: 1000
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
sourceComponent: ClippingRectangle {
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
radius: 1000
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: Qt.resolvedUrl(root.modelData.image)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
mipmap: true
|
||||
cache: false
|
||||
asynchronous: true
|
||||
}
|
||||
}
|
||||
}
|
||||
source: Qt.resolvedUrl(root.modelData.image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: appIcon
|
||||
Loader {
|
||||
id: appIcon
|
||||
|
||||
active: root.hasAppIcon || !root.hasImage
|
||||
|
||||
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
|
||||
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
|
||||
anchors.right: root.hasImage ? image.right : undefined
|
||||
anchors.bottom: root.hasImage ? image.bottom : undefined
|
||||
active: root.hasAppIcon || !root.hasImage
|
||||
anchors.bottom: root.hasImage ? image.bottom : undefined
|
||||
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
|
||||
anchors.right: root.hasImage ? image.right : undefined
|
||||
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
radius: 1000
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2) : DynamicColors.palette.m3secondaryContainer
|
||||
implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
|
||||
implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
|
||||
sourceComponent: CustomRect {
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2) : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
|
||||
implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
|
||||
radius: 1000
|
||||
|
||||
Loader {
|
||||
id: icon
|
||||
Loader {
|
||||
id: icon
|
||||
|
||||
active: root.hasAppIcon
|
||||
active: root.hasAppIcon
|
||||
anchors.centerIn: parent
|
||||
asynchronous: true
|
||||
height: Math.round(parent.width * 0.6)
|
||||
width: Math.round(parent.width * 0.6)
|
||||
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: CustomIcon {
|
||||
anchors.fill: parent
|
||||
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.modelData.appIcon)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !root.hasAppIcon
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -18 * 0.02
|
||||
anchors.verticalCenterOffset: 18 * 0.02
|
||||
asynchronous: true
|
||||
|
||||
width: Math.round(parent.width * 0.6)
|
||||
height: Math.round(parent.width * 0.6)
|
||||
sourceComponent: MaterialIcon {
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: CustomIcon {
|
||||
anchors.fill: parent
|
||||
source: Quickshell.iconPath(root.modelData.appIcon)
|
||||
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
id: appName
|
||||
|
||||
Loader {
|
||||
active: !root.hasAppIcon
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -18 * 0.02
|
||||
anchors.verticalCenterOffset: 18 * 0.02
|
||||
asynchronous: true
|
||||
anchors.left: image.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.top: parent.top
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
maximumLineCount: 1
|
||||
opacity: root.expanded ? 1 : 0
|
||||
text: appNameMetrics.elidedText
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: appNameMetrics
|
||||
|
||||
CustomText {
|
||||
id: appName
|
||||
elide: Text.ElideRight
|
||||
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
|
||||
font.family: appName.font.family
|
||||
font.pointSize: appName.font.pointSize
|
||||
text: root.modelData.appName
|
||||
}
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: image.right
|
||||
anchors.leftMargin: 10
|
||||
CustomText {
|
||||
id: summary
|
||||
|
||||
animate: true
|
||||
text: appNameMetrics.elidedText
|
||||
maximumLineCount: 1
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
anchors.left: image.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.top: parent.top
|
||||
animate: true
|
||||
height: implicitHeight
|
||||
maximumLineCount: 1
|
||||
text: summaryMetrics.elidedText
|
||||
|
||||
opacity: root.expanded ? 1 : 0
|
||||
Behavior on height {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
summary.maximumLineCount: undefined
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: appNameMetrics
|
||||
AnchorChanges {
|
||||
anchors.top: appName.bottom
|
||||
target: summary
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
PropertyAction {
|
||||
property: "maximumLineCount"
|
||||
target: summary
|
||||
}
|
||||
|
||||
text: root.modelData.appName
|
||||
font.family: appName.font.family
|
||||
font.pointSize: appName.font.pointSize
|
||||
elide: Text.ElideRight
|
||||
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: summary
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: image.right
|
||||
anchors.leftMargin: 10
|
||||
|
||||
animate: true
|
||||
text: summaryMetrics.elidedText
|
||||
maximumLineCount: 1
|
||||
height: implicitHeight
|
||||
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
PropertyChanges {
|
||||
summary.maximumLineCount: undefined
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
target: summary
|
||||
anchors.top: appName.bottom
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
PropertyAction {
|
||||
target: summary
|
||||
property: "maximumLineCount"
|
||||
}
|
||||
AnchorAnimation {
|
||||
easing.type: Easing.BezierSpline
|
||||
AnchorAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: summaryMetrics
|
||||
|
||||
text: root.modelData.summary
|
||||
font.family: summary.font.family
|
||||
font.pointSize: summary.font.pointSize
|
||||
elide: Text.ElideRight
|
||||
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: timeSep
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: summary.right
|
||||
anchors.leftMargin: 7
|
||||
|
||||
text: "•"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
AnchorChanges {
|
||||
target: timeSep
|
||||
anchors.left: appName.right
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: summaryMetrics
|
||||
|
||||
elide: Text.ElideRight
|
||||
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
|
||||
font.family: summary.font.family
|
||||
font.pointSize: summary.font.pointSize
|
||||
text: root.modelData.summary
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: timeSep
|
||||
|
||||
anchors.left: summary.right
|
||||
anchors.leftMargin: 7
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
text: "•"
|
||||
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
AnchorChanges {
|
||||
anchors.left: appName.right
|
||||
target: timeSep
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: time
|
||||
CustomText {
|
||||
id: time
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: timeSep.right
|
||||
anchors.leftMargin: 7
|
||||
anchors.left: timeSep.right
|
||||
anchors.leftMargin: 7
|
||||
anchors.top: parent.top
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: root.modelData.timeStr
|
||||
}
|
||||
|
||||
animate: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: root.modelData.timeStr
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
}
|
||||
Item {
|
||||
id: expandBtn
|
||||
|
||||
Item {
|
||||
id: expandBtn
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
implicitHeight: expandIcon.height
|
||||
implicitWidth: expandIcon.height
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
StateLayer {
|
||||
function onClicked() {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
|
||||
implicitWidth: expandIcon.height
|
||||
implicitHeight: expandIcon.height
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
radius: 1000
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: 1000
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
MaterialIcon {
|
||||
id: expandIcon
|
||||
|
||||
function onClicked() {
|
||||
root.expanded = !root.expanded;
|
||||
}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
font.pointSize: 13
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: expandIcon
|
||||
CustomText {
|
||||
id: bodyPreview
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.left: summary.left
|
||||
anchors.right: expandBtn.left
|
||||
anchors.rightMargin: 7
|
||||
anchors.top: summary.bottom
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
opacity: root.expanded ? 0 : 1
|
||||
text: bodyPreviewMetrics.elidedText
|
||||
textFormat: Text.MarkdownText
|
||||
|
||||
animate: true
|
||||
text: root.expanded ? "expand_less" : "expand_more"
|
||||
font.pointSize: 13
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: bodyPreview
|
||||
TextMetrics {
|
||||
id: bodyPreviewMetrics
|
||||
|
||||
anchors.left: summary.left
|
||||
anchors.right: expandBtn.left
|
||||
anchors.top: summary.bottom
|
||||
anchors.rightMargin: 7
|
||||
elide: Text.ElideRight
|
||||
elideWidth: bodyPreview.width
|
||||
font.family: bodyPreview.font.family
|
||||
font.pointSize: bodyPreview.font.pointSize
|
||||
text: root.modelData.body
|
||||
}
|
||||
|
||||
animate: true
|
||||
textFormat: Text.MarkdownText
|
||||
text: bodyPreviewMetrics.elidedText
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
CustomText {
|
||||
id: body
|
||||
|
||||
opacity: root.expanded ? 0 : 1
|
||||
anchors.left: summary.left
|
||||
anchors.right: expandBtn.left
|
||||
anchors.rightMargin: 7
|
||||
anchors.top: summary.bottom
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
height: text ? implicitHeight : 0
|
||||
opacity: root.expanded ? 1 : 0
|
||||
text: root.modelData.body
|
||||
textFormat: Text.MarkdownText
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: bodyPreviewMetrics
|
||||
onLinkActivated: link => {
|
||||
if (!root.expanded)
|
||||
return;
|
||||
|
||||
text: root.modelData.body
|
||||
font.family: bodyPreview.font.family
|
||||
font.pointSize: bodyPreview.font.pointSize
|
||||
elide: Text.ElideRight
|
||||
elideWidth: bodyPreview.width
|
||||
}
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
root.modelData.popup = false;
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: body
|
||||
RowLayout {
|
||||
id: actions
|
||||
|
||||
anchors.left: summary.left
|
||||
anchors.right: expandBtn.left
|
||||
anchors.top: summary.bottom
|
||||
anchors.rightMargin: 7
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: body.bottom
|
||||
anchors.topMargin: 7
|
||||
opacity: root.expanded ? 1 : 0
|
||||
spacing: 10
|
||||
|
||||
animate: true
|
||||
textFormat: Text.MarkdownText
|
||||
text: root.modelData.body
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
height: text ? implicitHeight : 0
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
onLinkActivated: link => {
|
||||
if (!root.expanded)
|
||||
return;
|
||||
Action {
|
||||
modelData: QtObject {
|
||||
readonly property string text: qsTr("Close")
|
||||
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
root.modelData.popup = false;
|
||||
}
|
||||
function invoke(): void {
|
||||
root.modelData.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opacity: root.expanded ? 1 : 0
|
||||
Repeater {
|
||||
model: root.modelData.actions
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
delegate: Component {
|
||||
Action {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: actions
|
||||
component Action: CustomRect {
|
||||
id: action
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: body.bottom
|
||||
anchors.topMargin: 7
|
||||
required property var modelData
|
||||
|
||||
spacing: 10
|
||||
Layout.preferredHeight: actionText.height + 4 * 2
|
||||
Layout.preferredWidth: actionText.width + 8 * 2
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
implicitHeight: actionText.height + 4 * 2
|
||||
implicitWidth: actionText.width + 8 * 2
|
||||
radius: 1000
|
||||
|
||||
opacity: root.expanded ? 1 : 0
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
action.modelData.invoke();
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurface
|
||||
radius: 1000
|
||||
}
|
||||
|
||||
Action {
|
||||
modelData: QtObject {
|
||||
readonly property string text: qsTr("Close")
|
||||
function invoke(): void {
|
||||
root.modelData.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomText {
|
||||
id: actionText
|
||||
|
||||
Repeater {
|
||||
model: root.modelData.actions
|
||||
anchors.centerIn: parent
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
text: actionTextMetrics.elidedText
|
||||
}
|
||||
|
||||
delegate: Component {
|
||||
Action {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: actionTextMetrics
|
||||
|
||||
component Action: CustomRect {
|
||||
id: action
|
||||
|
||||
required property var modelData
|
||||
|
||||
radius: 1000
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||
|
||||
Layout.preferredWidth: actionText.width + 8 * 2
|
||||
Layout.preferredHeight: actionText.height + 4 * 2
|
||||
implicitWidth: actionText.width + 8 * 2
|
||||
implicitHeight: actionText.height + 4 * 2
|
||||
|
||||
StateLayer {
|
||||
radius: 1000
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurface
|
||||
|
||||
function onClicked(): void {
|
||||
action.modelData.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: actionText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: actionTextMetrics.elidedText
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 10
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: actionTextMetrics
|
||||
|
||||
text: action.modelData.text
|
||||
font.family: actionText.font.family
|
||||
font.pointSize: actionText.font.pointSize
|
||||
elide: Text.ElideRight
|
||||
elideWidth: {
|
||||
const numActions = root.modelData.actions.length + 1;
|
||||
return (inner.width - actions.spacing * (numActions - 1)) / numActions - 8 * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
elide: Text.ElideRight
|
||||
elideWidth: {
|
||||
const numActions = root.modelData.actions.length + 1;
|
||||
return (inner.width - actions.spacing * (numActions - 1)) / numActions - 8 * 2;
|
||||
}
|
||||
font.family: actionText.font.family
|
||||
font.pointSize: actionText.font.pointSize
|
||||
text: action.modelData.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,50 +4,51 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var panels
|
||||
|
||||
readonly property real rounding: 8
|
||||
|
||||
readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
|
||||
readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
|
||||
|
||||
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
|
||||
readonly property real utilsRoundingX: utilsWidthDiff < rounding * 2 ? utilsWidthDiff / 2 : rounding
|
||||
id: root
|
||||
|
||||
readonly property bool flatten: wrapper.width < rounding * 2
|
||||
readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
|
||||
readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
|
||||
required property var panels
|
||||
readonly property real rounding: 8
|
||||
readonly property real utilsRoundingX: utilsWidthDiff < rounding * 2 ? utilsWidthDiff / 2 : rounding
|
||||
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: flatten ? "transparent" : DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathLine {
|
||||
relativeX: -root.wrapper.width - root.notifsRoundingX
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.notifsRoundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.notifsRoundingX
|
||||
radiusY: root.rounding
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.rounding * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: -root.utilsRoundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.utilsRoundingX
|
||||
radiusY: root.rounding
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width + root.utilsRoundingX
|
||||
relativeY: 0
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathLine {
|
||||
relativeX: -root.wrapper.width - root.notifsRoundingX
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.notifsRoundingX
|
||||
radiusY: root.rounding
|
||||
relativeX: root.notifsRoundingX
|
||||
relativeY: root.rounding
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.rounding * 2
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.utilsRoundingX
|
||||
radiusY: root.rounding
|
||||
relativeX: -root.utilsRoundingX
|
||||
relativeY: root.rounding
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width + root.utilsRoundingX
|
||||
relativeY: 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,36 +4,34 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Props props
|
||||
required property var visibilities
|
||||
required property Props props
|
||||
required property var visibilities
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
CustomRect {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLow
|
||||
radius: 8
|
||||
|
||||
radius: 8
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLow
|
||||
NotifDock {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
NotifDock {
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
Layout.topMargin: 8 - layout.spacing
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 1
|
||||
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
}
|
||||
}
|
||||
CustomRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8 - layout.spacing
|
||||
color: DynamicColors.tPalette.m3outlineVariant
|
||||
implicitHeight: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,158 +9,153 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property NotifServer.Notif modelData
|
||||
required property Props props
|
||||
required property bool expanded
|
||||
required property var visibilities
|
||||
readonly property CustomText body: expandedContent.item?.body ?? null
|
||||
required property bool expanded
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property real nonAnimHeight: expanded ? summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + 10 * 2 : summaryHeightMetrics.height
|
||||
required property Props props
|
||||
required property var visibilities
|
||||
|
||||
readonly property CustomText body: expandedContent.item?.body ?? null
|
||||
readonly property real nonAnimHeight: expanded ? summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + 10 * 2 : summaryHeightMetrics.height
|
||||
color: {
|
||||
const c = root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
|
||||
return expanded ? c : Qt.alpha(c, 0);
|
||||
}
|
||||
implicitHeight: nonAnimHeight
|
||||
radius: 6
|
||||
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
radius: 6
|
||||
color: {
|
||||
const c = root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
|
||||
return expanded ? c : Qt.alpha(c, 0);
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
PropertyChanges {
|
||||
summary.anchors.margins: 10
|
||||
dummySummary.anchors.margins: 10
|
||||
compactBody.anchors.margins: 10
|
||||
timeStr.anchors.margins: 10
|
||||
expandedContent.anchors.margins: 10
|
||||
summary.width: root.width - 10 * 2 - timeStr.implicitWidth - 7
|
||||
summary.maximumLineCount: Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
properties: "margins,width,maximumLineCount"
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: summaryHeightMetrics
|
||||
|
||||
font: summary.font
|
||||
text: " " // Use this height to prevent weird characters from changing the line height
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: summary
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
width: parent.width
|
||||
text: root.modelData.summary
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: dummySummary
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
visible: false
|
||||
text: root.modelData.summary
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: compactBody
|
||||
|
||||
shouldBeActive: !root.expanded
|
||||
anchors.top: parent.top
|
||||
anchors.left: dummySummary.right
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 7
|
||||
|
||||
sourceComponent: CustomText {
|
||||
textFormat: Text.StyledText
|
||||
text: root.modelData.body.replace(/\n/g, " ")
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: timeStr
|
||||
|
||||
shouldBeActive: root.expanded
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
sourceComponent: CustomText {
|
||||
animate: true
|
||||
text: root.modelData.timeStr
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 11
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: expandedContent
|
||||
|
||||
shouldBeActive: root.expanded
|
||||
anchors.top: summary.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 7 / 2
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
readonly property alias body: body
|
||||
|
||||
spacing: 10
|
||||
|
||||
CustomText {
|
||||
id: body
|
||||
|
||||
Layout.fillWidth: true
|
||||
textFormat: Text.MarkdownText
|
||||
text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body given")
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurface
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
onLinkActivated: link => {
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
root.visibilities.sidebar = false;
|
||||
}
|
||||
}
|
||||
|
||||
NotifActionList {
|
||||
notif: root.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
states: State {
|
||||
name: "expanded"
|
||||
when: root.expanded
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property bool shouldBeActive
|
||||
PropertyChanges {
|
||||
compactBody.anchors.margins: 10
|
||||
dummySummary.anchors.margins: 10
|
||||
expandedContent.anchors.margins: 10
|
||||
summary.anchors.margins: 10
|
||||
summary.maximumLineCount: Number.MAX_SAFE_INTEGER
|
||||
summary.width: root.width - 10 * 2 - timeStr.implicitWidth - 7
|
||||
timeStr.anchors.margins: 10
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
properties: "margins,width,maximumLineCount"
|
||||
}
|
||||
}
|
||||
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
active: opacity > 0
|
||||
TextMetrics {
|
||||
id: summaryHeightMetrics
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
font: summary.font
|
||||
text: " " // Use this height to prevent weird characters from changing the line height
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: summary
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
text: root.modelData.summary
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: dummySummary
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
text: root.modelData.summary
|
||||
visible: false
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: compactBody
|
||||
|
||||
anchors.left: dummySummary.right
|
||||
anchors.leftMargin: 7
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
shouldBeActive: !root.expanded
|
||||
|
||||
sourceComponent: CustomText {
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
text: root.modelData.body.replace(/\n/g, " ")
|
||||
textFormat: Text.StyledText
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: timeStr
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
shouldBeActive: root.expanded
|
||||
|
||||
sourceComponent: CustomText {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 11
|
||||
text: root.modelData.timeStr
|
||||
}
|
||||
}
|
||||
|
||||
WrappedLoader {
|
||||
id: expandedContent
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: summary.bottom
|
||||
anchors.topMargin: 7 / 2
|
||||
shouldBeActive: root.expanded
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
readonly property alias body: body
|
||||
|
||||
spacing: 10
|
||||
|
||||
CustomText {
|
||||
id: body
|
||||
|
||||
Layout.fillWidth: true
|
||||
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurface
|
||||
text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body given")
|
||||
textFormat: Text.MarkdownText
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
onLinkActivated: link => {
|
||||
Quickshell.execDetached(["app2unit", "-O", "--", link]);
|
||||
root.visibilities.sidebar = false;
|
||||
}
|
||||
}
|
||||
|
||||
NotifActionList {
|
||||
notif: root.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property bool shouldBeActive
|
||||
|
||||
active: opacity > 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,125 +10,124 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property NotifServer.Notif notif
|
||||
required property NotifServer.Notif notif
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: flickable.contentHeight
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: flickable.contentHeight
|
||||
|
||||
CustomFlickable {
|
||||
id: flickable
|
||||
CustomFlickable {
|
||||
id: flickable
|
||||
|
||||
anchors.fill: parent
|
||||
contentWidth: Math.max(width, actionList.implicitWidth)
|
||||
contentHeight: actionList.implicitHeight
|
||||
anchors.fill: parent
|
||||
contentHeight: actionList.implicitHeight
|
||||
contentWidth: Math.max(width, actionList.implicitWidth)
|
||||
|
||||
RowLayout {
|
||||
id: actionList
|
||||
RowLayout {
|
||||
id: actionList
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 7
|
||||
anchors.fill: parent
|
||||
spacing: 7
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
isClose: true
|
||||
},
|
||||
...root.notif.actions,
|
||||
{
|
||||
isCopy: true
|
||||
}
|
||||
]
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
isClose: true
|
||||
},
|
||||
...root.notif.actions,
|
||||
{
|
||||
isCopy: true
|
||||
}
|
||||
]
|
||||
|
||||
CustomRect {
|
||||
id: action
|
||||
CustomRect {
|
||||
id: action
|
||||
|
||||
required property var modelData
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: actionInner.implicitWidth + 5 * 2
|
||||
implicitHeight: actionInner.implicitHeight + 5 * 2
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (actionStateLayer.pressed ? 18 : 0)
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 4)
|
||||
implicitHeight: actionInner.implicitHeight + 5 * 2
|
||||
implicitWidth: actionInner.implicitWidth + 5 * 2
|
||||
radius: actionStateLayer.pressed ? 6 / 2 : 6
|
||||
|
||||
Layout.preferredWidth: implicitWidth + (actionStateLayer.pressed ? 18 : 0)
|
||||
radius: actionStateLayer.pressed ? 6 / 2 : 6
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 4)
|
||||
|
||||
Timer {
|
||||
id: copyTimer
|
||||
|
||||
interval: 1000
|
||||
onTriggered: actionInner.item.text = "content_copy"
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: actionStateLayer
|
||||
|
||||
function onClicked(): void {
|
||||
if (action.modelData.isClose) {
|
||||
root.notif.close();
|
||||
} else if (action.modelData.isCopy) {
|
||||
Quickshell.clipboardText = root.notif.body;
|
||||
actionInner.item.text = "inventory";
|
||||
copyTimer.start();
|
||||
} else if (action.modelData.invoke) {
|
||||
action.modelData.invoke();
|
||||
} else if (!root.notif.resident) {
|
||||
root.notif.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: actionInner
|
||||
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: action.modelData.isClose || action.modelData.isCopy ? iconBtn : root.notif.hasActionIcons ? iconComp : textComp
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconBtn
|
||||
|
||||
MaterialIcon {
|
||||
animate: action.modelData.isCopy ?? false
|
||||
text: action.modelData.isCopy ? "content_copy" : "close"
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconComp
|
||||
|
||||
IconImage {
|
||||
source: Quickshell.iconPath(action.modelData.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: textComp
|
||||
|
||||
CustomText {
|
||||
text: action.modelData.text
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: copyTimer
|
||||
|
||||
interval: 1000
|
||||
|
||||
onTriggered: actionInner.item.text = "content_copy"
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: actionStateLayer
|
||||
|
||||
function onClicked(): void {
|
||||
if (action.modelData.isClose) {
|
||||
root.notif.close();
|
||||
} else if (action.modelData.isCopy) {
|
||||
Quickshell.clipboardText = root.notif.body;
|
||||
actionInner.item.text = "inventory";
|
||||
copyTimer.start();
|
||||
} else if (action.modelData.invoke) {
|
||||
action.modelData.invoke();
|
||||
} else if (!root.notif.resident) {
|
||||
root.notif.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: actionInner
|
||||
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: action.modelData.isClose || action.modelData.isCopy ? iconBtn : root.notif.hasActionIcons ? iconComp : textComp
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconBtn
|
||||
|
||||
MaterialIcon {
|
||||
animate: action.modelData.isCopy ?? false
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: action.modelData.isCopy ? "content_copy" : "close"
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconComp
|
||||
|
||||
IconImage {
|
||||
source: Quickshell.iconPath(action.modelData.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: textComp
|
||||
|
||||
CustomText {
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
text: action.modelData.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,183 +10,177 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Props props
|
||||
required property var visibilities
|
||||
readonly property int notifCount: NotifServer.list.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
|
||||
readonly property int notifCount: NotifServer.list.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
|
||||
required property Props props
|
||||
required property var visibilities
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
|
||||
Component.onCompleted: NotifServer.list.forEach(n => n.popup = false)
|
||||
Component.onCompleted: NotifServer.list.forEach(n => n.popup = false)
|
||||
|
||||
Item {
|
||||
id: title
|
||||
Item {
|
||||
id: title
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 4
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 4
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
implicitHeight: Math.max(count.implicitHeight, titleText.implicitHeight)
|
||||
|
||||
implicitHeight: Math.max(count.implicitHeight, titleText.implicitHeight)
|
||||
CustomText {
|
||||
id: count
|
||||
|
||||
CustomText {
|
||||
id: count
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.notifCount > 0 ? 0 : -width - titleText.anchors.leftMargin
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.pointSize: 13
|
||||
font.weight: 500
|
||||
opacity: root.notifCount > 0 ? 1 : 0
|
||||
text: root.notifCount
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.notifCount > 0 ? 0 : -width - titleText.anchors.leftMargin
|
||||
opacity: root.notifCount > 0 ? 1 : 0
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: root.notifCount
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 13
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.weight: 500
|
||||
CustomText {
|
||||
id: titleText
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {}
|
||||
}
|
||||
anchors.left: count.right
|
||||
anchors.leftMargin: 7
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3outline
|
||||
elide: Text.ElideRight
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.pointSize: 13
|
||||
font.weight: 500
|
||||
text: root.notifCount > 0 ? qsTr("notification%1").arg(root.notifCount === 1 ? "" : "s") : qsTr("Notifications")
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
|
||||
CustomText {
|
||||
id: titleText
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 10
|
||||
color: "transparent"
|
||||
radius: 6
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: count.right
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 7
|
||||
Loader {
|
||||
active: opacity > 0
|
||||
anchors.centerIn: parent
|
||||
opacity: root.notifCount > 0 ? 0 : 1
|
||||
|
||||
text: root.notifCount > 0 ? qsTr("notification%1").arg(root.notifCount === 1 ? "" : "s") : qsTr("Notifications")
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 13
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.weight: 500
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
ClippingRectangle {
|
||||
id: clipRect
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: title.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.topMargin: 10
|
||||
|
||||
radius: 6
|
||||
color: "transparent"
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: opacity > 0
|
||||
opacity: root.notifCount > 0 ? 0 : 1
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 20
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("No Notifications")
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.pointSize: 18
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.weight: 500
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 20
|
||||
|
||||
CustomFlickable {
|
||||
id: view
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
color: DynamicColors.palette.m3outlineVariant
|
||||
font.family: "CaskaydiaCove NF"
|
||||
font.pointSize: 18
|
||||
font.weight: 500
|
||||
text: qsTr("No Notifications")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
CustomFlickable {
|
||||
id: view
|
||||
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentWidth: width
|
||||
contentHeight: notifList.implicitHeight
|
||||
anchors.fill: parent
|
||||
contentHeight: notifList.implicitHeight
|
||||
contentWidth: width
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: view
|
||||
}
|
||||
CustomScrollBar.vertical: CustomScrollBar {
|
||||
flickable: view
|
||||
}
|
||||
|
||||
NotifDockList {
|
||||
id: notifList
|
||||
NotifDockList {
|
||||
id: notifList
|
||||
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
container: view
|
||||
}
|
||||
}
|
||||
}
|
||||
container: view
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: clearTimer
|
||||
Timer {
|
||||
id: clearTimer
|
||||
|
||||
repeat: true
|
||||
interval: 50
|
||||
onTriggered: {
|
||||
let next = null;
|
||||
for (let i = 0; i < notifList.repeater.count; i++) {
|
||||
next = notifList.repeater.itemAt(i);
|
||||
if (!next?.closed)
|
||||
break;
|
||||
}
|
||||
if (next)
|
||||
next.closeAll();
|
||||
else
|
||||
stop();
|
||||
}
|
||||
}
|
||||
interval: 50
|
||||
repeat: true
|
||||
|
||||
Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 8
|
||||
onTriggered: {
|
||||
let next = null;
|
||||
for (let i = 0; i < notifList.repeater.count; i++) {
|
||||
next = notifList.repeater.itemAt(i);
|
||||
if (!next?.closed)
|
||||
break;
|
||||
}
|
||||
if (next)
|
||||
next.closeAll();
|
||||
else
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
scale: root.notifCount > 0 ? 1 : 0.5
|
||||
opacity: root.notifCount > 0 ? 1 : 0
|
||||
active: opacity > 0
|
||||
Loader {
|
||||
active: opacity > 0
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 8
|
||||
anchors.right: parent.right
|
||||
opacity: root.notifCount > 0 ? 1 : 0
|
||||
scale: root.notifCount > 0 ? 1 : 0.5
|
||||
|
||||
sourceComponent: IconButton {
|
||||
id: clearBtn
|
||||
|
||||
icon: "clear_all"
|
||||
radius: 8
|
||||
padding: 8
|
||||
font.pointSize: Math.round(18 * 1.2)
|
||||
onClicked: clearTimer.start()
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
level: clearBtn.stateLayer.containsMouse ? 4 : 3
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceComponent: IconButton {
|
||||
id: clearBtn
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
}
|
||||
font.pointSize: Math.round(18 * 1.2)
|
||||
icon: "clear_all"
|
||||
padding: 8
|
||||
radius: 8
|
||||
|
||||
onClicked: clearTimer.start()
|
||||
|
||||
Elevation {
|
||||
anchors.fill: parent
|
||||
level: clearBtn.stateLayer.containsMouse ? 4 : 3
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,162 +8,158 @@ import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Props props
|
||||
required property Flickable container
|
||||
required property var visibilities
|
||||
required property Flickable container
|
||||
property bool flag
|
||||
required property Props props
|
||||
readonly property alias repeater: repeater
|
||||
readonly property int spacing: 8
|
||||
required property var visibilities
|
||||
|
||||
readonly property alias repeater: repeater
|
||||
readonly property int spacing: 8
|
||||
property bool flag
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: {
|
||||
const item = repeater.itemAt(repeater.count - 1);
|
||||
return item ? item.y + item.implicitHeight : 0;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
implicitHeight: {
|
||||
const item = repeater.itemAt(repeater.count - 1);
|
||||
return item ? item.y + item.implicitHeight : 0;
|
||||
}
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const map = new Map();
|
||||
for (const n of NotifServer.notClosed)
|
||||
map.set(n.appName, null);
|
||||
for (const n of NotifServer.list)
|
||||
map.set(n.appName, null);
|
||||
return [...map.keys()];
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const map = new Map();
|
||||
for (const n of NotifServer.notClosed)
|
||||
map.set(n.appName, null);
|
||||
for (const n of NotifServer.list)
|
||||
map.set(n.appName, null);
|
||||
return [...map.keys()];
|
||||
}
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: notif
|
||||
MouseArea {
|
||||
id: notif
|
||||
|
||||
required property int index
|
||||
required property string modelData
|
||||
readonly property bool closed: notifInner.notifCount === 0
|
||||
required property int index
|
||||
required property string modelData
|
||||
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
|
||||
property int startY
|
||||
|
||||
readonly property bool closed: notifInner.notifCount === 0
|
||||
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
|
||||
property int startY
|
||||
|
||||
function closeAll(): void {
|
||||
for (const n of NotifServer.notClosed.filter(n => n.appName === modelData)) {
|
||||
n.close();
|
||||
function closeAll(): void {
|
||||
for (const n of NotifServer.notClosed.filter(n => n.appName === modelData)) {
|
||||
n.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
y: {
|
||||
root.flag; // Force update
|
||||
let y = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.closed)
|
||||
y += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
cursorShape: pressed ? Qt.ClosedHandCursor : undefined
|
||||
drag.axis: Drag.XAxis
|
||||
drag.target: this
|
||||
enabled: !closed
|
||||
hoverEnabled: true
|
||||
implicitHeight: notifInner.implicitHeight
|
||||
implicitWidth: root.width
|
||||
preventStealing: true
|
||||
y: {
|
||||
root.flag; // Force update
|
||||
let y = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.closed)
|
||||
y += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
containmentMask: QtObject {
|
||||
function contains(p: point): bool {
|
||||
if (!root.container.contains(notif.mapToItem(root.container, p)))
|
||||
return false;
|
||||
return notifInner.contains(p);
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: root.width
|
||||
implicitHeight: notifInner.implicitHeight
|
||||
|
||||
hoverEnabled: true
|
||||
cursorShape: pressed ? Qt.ClosedHandCursor : undefined
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
preventStealing: true
|
||||
enabled: !closed
|
||||
|
||||
drag.target: this
|
||||
drag.axis: Drag.XAxis
|
||||
|
||||
onPressed: event => {
|
||||
startY = event.y;
|
||||
if (event.button === Qt.RightButton)
|
||||
notifInner.toggleExpand(!notifInner.expanded);
|
||||
else if (event.button === Qt.MiddleButton)
|
||||
closeAll();
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
notifInner.toggleExpand(diffY > 0);
|
||||
}
|
||||
}
|
||||
onReleased: event => {
|
||||
if (Math.abs(x) < width * Config.notifs.clearThreshold)
|
||||
x = 0;
|
||||
else
|
||||
closeAll();
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
containmentMask: QtObject {
|
||||
function contains(p: point): bool {
|
||||
if (!root.container.contains(notif.mapToItem(root.container, p)))
|
||||
return false;
|
||||
return notifInner.contains(p);
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.closed
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
NotifGroup {
|
||||
id: notifInner
|
||||
|
||||
modelData: notif.modelData
|
||||
props: root.props
|
||||
container: root.container
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
onPositionChanged: event => {
|
||||
if (pressed) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
notifInner.toggleExpand(diffY > 0);
|
||||
}
|
||||
}
|
||||
onPressed: event => {
|
||||
startY = event.y;
|
||||
if (event.button === Qt.RightButton)
|
||||
notifInner.toggleExpand(!notifInner.expanded);
|
||||
else if (event.button === Qt.MiddleButton)
|
||||
closeAll();
|
||||
}
|
||||
onReleased: event => {
|
||||
if (Math.abs(x) < width * Config.notifs.clearThreshold)
|
||||
x = 0;
|
||||
else
|
||||
closeAll();
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: true
|
||||
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
from: 0
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.closed
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
NotifGroup {
|
||||
id: notifInner
|
||||
|
||||
container: root.container
|
||||
modelData: notif.modelData
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,229 +11,224 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property string modelData
|
||||
required property Props props
|
||||
required property Flickable container
|
||||
required property var visibilities
|
||||
readonly property string appIcon: notifs.find(n => !n.closed && n.appIcon.length > 0)?.appIcon ?? ""
|
||||
required property Flickable container
|
||||
readonly property bool expanded: props.expandedNotifs.includes(modelData)
|
||||
readonly property string image: notifs.find(n => !n.closed && n.image.length > 0)?.image ?? ""
|
||||
required property string modelData
|
||||
readonly property int nonAnimHeight: {
|
||||
const headerHeight = header.implicitHeight + (root.expanded ? Math.round(7 / 2) : 0);
|
||||
const columnHeight = headerHeight + notifList.nonAnimHeight + column.Layout.topMargin + column.Layout.bottomMargin;
|
||||
return Math.round(Math.max(Config.notifs.sizes.image, columnHeight) + 10 * 2);
|
||||
}
|
||||
readonly property int notifCount: notifs.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
|
||||
readonly property list<var> notifs: NotifServer.list.filter(n => n.appName === modelData)
|
||||
required property Props props
|
||||
readonly property int urgency: notifs.some(n => !n.closed && n.urgency === NotificationUrgency.Critical) ? NotificationUrgency.Critical : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? NotificationUrgency.Normal : NotificationUrgency.Low
|
||||
required property var visibilities
|
||||
|
||||
readonly property list<var> notifs: NotifServer.list.filter(n => n.appName === modelData)
|
||||
readonly property int notifCount: notifs.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
|
||||
readonly property string image: notifs.find(n => !n.closed && n.image.length > 0)?.image ?? ""
|
||||
readonly property string appIcon: notifs.find(n => !n.closed && n.appIcon.length > 0)?.appIcon ?? ""
|
||||
readonly property int urgency: notifs.some(n => !n.closed && n.urgency === NotificationUrgency.Critical) ? NotificationUrgency.Critical : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? NotificationUrgency.Normal : NotificationUrgency.Low
|
||||
function toggleExpand(expand: bool): void {
|
||||
if (expand) {
|
||||
if (!expanded)
|
||||
props.expandedNotifs.push(modelData);
|
||||
} else if (expanded) {
|
||||
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
|
||||
}
|
||||
}
|
||||
|
||||
readonly property int nonAnimHeight: {
|
||||
const headerHeight = header.implicitHeight + (root.expanded ? Math.round(7 / 2) : 0);
|
||||
const columnHeight = headerHeight + notifList.nonAnimHeight + column.Layout.topMargin + column.Layout.bottomMargin;
|
||||
return Math.round(Math.max(Config.notifs.sizes.image, columnHeight) + 10 * 2);
|
||||
}
|
||||
readonly property bool expanded: props.expandedNotifs.includes(modelData)
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
clip: true
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||
implicitHeight: content.implicitHeight + 10 * 2
|
||||
radius: 8
|
||||
|
||||
function toggleExpand(expand: bool): void {
|
||||
if (expand) {
|
||||
if (!expanded)
|
||||
props.expandedNotifs.push(modelData);
|
||||
} else if (expanded) {
|
||||
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
if (notifCount === 0 && expanded)
|
||||
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
if (notifCount === 0 && expanded)
|
||||
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
|
||||
}
|
||||
RowLayout {
|
||||
id: content
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: content.implicitHeight + 10 * 2
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 10
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: 10
|
||||
|
||||
clip: true
|
||||
radius: 8
|
||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
|
||||
RowLayout {
|
||||
id: content
|
||||
Component {
|
||||
id: imageComp
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.margins: 10
|
||||
Image {
|
||||
asynchronous: true
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
height: Config.notifs.sizes.image
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
width: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
|
||||
spacing: 10
|
||||
Component {
|
||||
id: appIconComp
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
CustomIcon {
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: imageComp
|
||||
Component {
|
||||
id: materialIconComp
|
||||
|
||||
Image {
|
||||
source: Qt.resolvedUrl(root.image)
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
cache: false
|
||||
asynchronous: true
|
||||
width: Config.notifs.sizes.image
|
||||
height: Config.notifs.sizes.image
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appIconComp
|
||||
CustomClippingRect {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: 1000
|
||||
|
||||
CustomIcon {
|
||||
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: materialIconComp
|
||||
Loader {
|
||||
active: root.appIcon && root.image
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
MaterialIcon {
|
||||
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
}
|
||||
}
|
||||
sourceComponent: CustomRect {
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
radius: 1000
|
||||
|
||||
CustomClippingRect {
|
||||
anchors.fill: parent
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3) : DynamicColors.palette.m3secondaryContainer
|
||||
radius: 1000
|
||||
CustomIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
id: column
|
||||
|
||||
Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
active: root.appIcon && root.image
|
||||
Layout.bottomMargin: -10 / 2
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -10
|
||||
spacing: 0
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
implicitWidth: Config.notifs.sizes.badge
|
||||
implicitHeight: Config.notifs.sizes.badge
|
||||
RowLayout {
|
||||
id: header
|
||||
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3secondaryContainer
|
||||
radius: 1000
|
||||
Layout.bottomMargin: root.expanded ? Math.round(7 / 2) : 0
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
CustomIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
|
||||
source: Quickshell.iconPath(root.appIcon)
|
||||
layer.enabled: root.appIcon.endsWith("symbolic")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on Layout.bottomMargin {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: column
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: 11
|
||||
text: root.modelData
|
||||
}
|
||||
|
||||
Layout.topMargin: -10
|
||||
Layout.bottomMargin: -10 / 2
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
CustomText {
|
||||
animate: true
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 11
|
||||
text: root.notifs.find(n => !n.closed)?.timeStr ?? ""
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: header
|
||||
CustomRect {
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3)
|
||||
implicitHeight: groupCount.implicitHeight + 10
|
||||
implicitWidth: expandBtn.implicitWidth + 7 * 2
|
||||
radius: 1000
|
||||
|
||||
Layout.bottomMargin: root.expanded ? Math.round(7 / 2) : 0
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.toggleExpand(!root.expanded);
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: root.modelData
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 11
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
}
|
||||
|
||||
CustomText {
|
||||
animate: true
|
||||
text: root.notifs.find(n => !n.closed)?.timeStr ?? ""
|
||||
color: DynamicColors.palette.m3outline
|
||||
font.pointSize: 11
|
||||
}
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: expandBtn.implicitWidth + 7 * 2
|
||||
implicitHeight: groupCount.implicitHeight + 10
|
||||
anchors.centerIn: parent
|
||||
spacing: 7 / 2
|
||||
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3)
|
||||
radius: 1000
|
||||
CustomText {
|
||||
id: groupCount
|
||||
|
||||
StateLayer {
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
Layout.leftMargin: 10 / 2
|
||||
animate: true
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 11
|
||||
text: root.notifCount
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
root.toggleExpand(!root.expanded);
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -10 / 2
|
||||
Layout.topMargin: root.expanded ? -Math.floor(7 / 2) : 0
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
rotation: root.expanded ? 180 : 0
|
||||
text: "expand_more"
|
||||
|
||||
RowLayout {
|
||||
id: expandBtn
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 7 / 2
|
||||
|
||||
CustomText {
|
||||
id: groupCount
|
||||
|
||||
Layout.leftMargin: 10 / 2
|
||||
animate: true
|
||||
text: root.notifCount
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 11
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.rightMargin: -10 / 2
|
||||
text: "expand_more"
|
||||
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
|
||||
rotation: root.expanded ? 180 : 0
|
||||
Layout.topMargin: root.expanded ? -Math.floor(7 / 2) : 0
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {
|
||||
Behavior on Layout.topMargin {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.topMargin {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on rotation {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on Layout.bottomMargin {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
NotifGroupList {
|
||||
id: notifList
|
||||
|
||||
NotifGroupList {
|
||||
id: notifList
|
||||
container: root.container
|
||||
expanded: root.expanded
|
||||
notifs: root.notifs
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
|
||||
props: root.props
|
||||
notifs: root.notifs
|
||||
expanded: root.expanded
|
||||
container: root.container
|
||||
visibilities: root.visibilities
|
||||
onRequestToggleExpand: expand => root.toggleExpand(expand)
|
||||
}
|
||||
}
|
||||
}
|
||||
onRequestToggleExpand: expand => root.toggleExpand(expand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,206 +9,201 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Props props
|
||||
required property list<var> notifs
|
||||
required property bool expanded
|
||||
required property Flickable container
|
||||
required property var visibilities
|
||||
required property Flickable container
|
||||
required property bool expanded
|
||||
property bool flag
|
||||
readonly property real nonAnimHeight: {
|
||||
let h = -root.spacing;
|
||||
for (let i = 0; i < repeater.count; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.modelData.closed && !item.previewHidden)
|
||||
h += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
required property list<var> notifs
|
||||
required property Props props
|
||||
property bool showAllNotifs
|
||||
readonly property int spacing: Math.round(7 / 2)
|
||||
required property var visibilities
|
||||
|
||||
readonly property real nonAnimHeight: {
|
||||
let h = -root.spacing;
|
||||
for (let i = 0; i < repeater.count; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.modelData.closed && !item.previewHidden)
|
||||
h += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
signal requestToggleExpand(expand: bool)
|
||||
|
||||
readonly property int spacing: Math.round(7 / 2)
|
||||
property bool showAllNotifs
|
||||
property bool flag
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
signal requestToggleExpand(expand: bool)
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded) {
|
||||
clearTimer.stop();
|
||||
showAllNotifs = true;
|
||||
} else {
|
||||
clearTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
Timer {
|
||||
id: clearTimer
|
||||
|
||||
interval: MaterialEasing.standardTime
|
||||
onTriggered: root.showAllNotifs = false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.showAllNotifs ? root.notifs : root.notifs.slice(0, Config.notifs.groupPreviewNum + 1)
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: notif
|
||||
|
||||
required property int index
|
||||
required property NotifServer.Notif modelData
|
||||
|
||||
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
|
||||
readonly property bool previewHidden: {
|
||||
if (root.expanded)
|
||||
return false;
|
||||
|
||||
let extraHidden = 0;
|
||||
for (let i = 0; i < index; i++)
|
||||
if (root.notifs[i].closed)
|
||||
extraHidden++;
|
||||
|
||||
return index >= Config.notifs.groupPreviewNum + extraHidden;
|
||||
}
|
||||
property int startY
|
||||
|
||||
y: {
|
||||
root.flag; // Force update
|
||||
let y = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.modelData.closed && !item.previewHidden)
|
||||
y += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
containmentMask: QtObject {
|
||||
function contains(p: point): bool {
|
||||
if (!root.container.contains(notif.mapToItem(root.container, p)))
|
||||
return false;
|
||||
return notifInner.contains(p);
|
||||
}
|
||||
}
|
||||
|
||||
opacity: previewHidden ? 0 : 1
|
||||
scale: previewHidden ? 0.7 : 1
|
||||
|
||||
implicitWidth: root.width
|
||||
implicitHeight: notifInner.implicitHeight
|
||||
|
||||
hoverEnabled: true
|
||||
cursorShape: notifInner.body?.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
preventStealing: !root.expanded
|
||||
enabled: !modelData.closed
|
||||
|
||||
drag.target: this
|
||||
drag.axis: Drag.XAxis
|
||||
|
||||
onPressed: event => {
|
||||
startY = event.y;
|
||||
if (event.button === Qt.RightButton)
|
||||
root.requestToggleExpand(!root.expanded);
|
||||
else if (event.button === Qt.MiddleButton)
|
||||
modelData.close();
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed && !root.expanded) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
root.requestToggleExpand(diffY > 0);
|
||||
}
|
||||
}
|
||||
onReleased: event => {
|
||||
if (Math.abs(x) < width * Config.notifs.clearThreshold)
|
||||
x = 0;
|
||||
else
|
||||
modelData.close();
|
||||
}
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
|
||||
ParallelAnimation {
|
||||
Component.onCompleted: running = !notif.previewHidden
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "scale"
|
||||
from: 0.7
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
|
||||
Anim {
|
||||
target: notif
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: notif
|
||||
property: "x"
|
||||
to: notif.x >= 0 ? notif.width : -notif.width
|
||||
}
|
||||
}
|
||||
|
||||
Notif {
|
||||
id: notifInner
|
||||
|
||||
anchors.fill: parent
|
||||
modelData: notif.modelData
|
||||
props: root.props
|
||||
expanded: root.expanded
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExpandedChanged: {
|
||||
if (expanded) {
|
||||
clearTimer.stop();
|
||||
showAllNotifs = true;
|
||||
} else {
|
||||
clearTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: clearTimer
|
||||
|
||||
interval: MaterialEasing.standardTime
|
||||
|
||||
onTriggered: root.showAllNotifs = false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.showAllNotifs ? root.notifs : root.notifs.slice(0, Config.notifs.groupPreviewNum + 1)
|
||||
|
||||
onValuesChanged: root.flagChanged()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: notif
|
||||
|
||||
required property int index
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
|
||||
readonly property bool previewHidden: {
|
||||
if (root.expanded)
|
||||
return false;
|
||||
|
||||
let extraHidden = 0;
|
||||
for (let i = 0; i < index; i++)
|
||||
if (root.notifs[i].closed)
|
||||
extraHidden++;
|
||||
|
||||
return index >= Config.notifs.groupPreviewNum + extraHidden;
|
||||
}
|
||||
property int startY
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
cursorShape: notifInner.body?.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
|
||||
drag.axis: Drag.XAxis
|
||||
drag.target: this
|
||||
enabled: !modelData.closed
|
||||
hoverEnabled: true
|
||||
implicitHeight: notifInner.implicitHeight
|
||||
implicitWidth: root.width
|
||||
opacity: previewHidden ? 0 : 1
|
||||
preventStealing: !root.expanded
|
||||
scale: previewHidden ? 0.7 : 1
|
||||
y: {
|
||||
root.flag; // Force update
|
||||
let y = 0;
|
||||
for (let i = 0; i < index; i++) {
|
||||
const item = repeater.itemAt(i);
|
||||
if (!item.modelData.closed && !item.previewHidden)
|
||||
y += item.nonAnimHeight + root.spacing;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
containmentMask: QtObject {
|
||||
function contains(p: point): bool {
|
||||
if (!root.container.contains(notif.mapToItem(root.container, p)))
|
||||
return false;
|
||||
return notifInner.contains(p);
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: modelData.lock(this)
|
||||
Component.onDestruction: modelData.unlock(this)
|
||||
onPositionChanged: event => {
|
||||
if (pressed && !root.expanded) {
|
||||
const diffY = event.y - startY;
|
||||
if (Math.abs(diffY) > Config.notifs.expandThreshold)
|
||||
root.requestToggleExpand(diffY > 0);
|
||||
}
|
||||
}
|
||||
onPressed: event => {
|
||||
startY = event.y;
|
||||
if (event.button === Qt.RightButton)
|
||||
root.requestToggleExpand(!root.expanded);
|
||||
else if (event.button === Qt.MiddleButton)
|
||||
modelData.close();
|
||||
}
|
||||
onReleased: event => {
|
||||
if (Math.abs(x) < width * Config.notifs.clearThreshold)
|
||||
x = 0;
|
||||
else
|
||||
modelData.close();
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
Component.onCompleted: running = !notif.previewHidden
|
||||
|
||||
Anim {
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
from: 0.7
|
||||
property: "scale"
|
||||
target: notif
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
running: notif.modelData.closed
|
||||
|
||||
onFinished: notif.modelData.unlock(notif)
|
||||
|
||||
Anim {
|
||||
property: "opacity"
|
||||
target: notif
|
||||
to: 0
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "x"
|
||||
target: notif
|
||||
to: notif.x >= 0 ? notif.width : -notif.width
|
||||
}
|
||||
}
|
||||
|
||||
Notif {
|
||||
id: notifInner
|
||||
|
||||
anchors.fill: parent
|
||||
expanded: root.expanded
|
||||
modelData: notif.modelData
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Quickshell
|
||||
|
||||
PersistentProperties {
|
||||
property list<string> expandedNotifs: []
|
||||
property list<string> expandedNotifs: []
|
||||
|
||||
reloadableId: "sidebar"
|
||||
reloadableId: "sidebar"
|
||||
}
|
||||
|
||||
@@ -4,51 +4,57 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var sidebar
|
||||
readonly property real rounding: 8
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: 8
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property var sidebar
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width + root.rounding)
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.sidebar.utilsRoundingX
|
||||
relativeY: -root.roundingY
|
||||
radiusX: root.sidebar.utilsRoundingX
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.rounding
|
||||
radiusX: root.rounding
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width + root.rounding)
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.sidebar.utilsRoundingX
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.sidebar.utilsRoundingX
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: root.rounding
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.rounding
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,75 +8,80 @@ import qs.Modules
|
||||
import qs.Daemons
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property Item popouts
|
||||
required property Item popouts
|
||||
required property var visibilities
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + 18 * 2
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: layout.implicitHeight + 18 * 2
|
||||
radius: 8
|
||||
|
||||
radius: 8
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 10
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 10
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 7
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 7
|
||||
|
||||
Toggle {
|
||||
visible: QSNetwork.Networking.devices.values.length > 0
|
||||
icon: Network.wifiEnabled ? "wifi" : "wifi_off"
|
||||
checked: Network.wifiEnabled
|
||||
icon: Network.wifiEnabled ? "wifi" : "wifi_off"
|
||||
visible: QSNetwork.Networking.devices.values.length > 0
|
||||
|
||||
onClicked: Network.toggleWifi()
|
||||
}
|
||||
|
||||
Toggle {
|
||||
Toggle {
|
||||
id: toggle
|
||||
icon: NotifServer.dnd ? "notifications_off" : "notifications"
|
||||
checked: !NotifServer.dnd
|
||||
onClicked: NotifServer.dnd = !NotifServer.dnd
|
||||
}
|
||||
|
||||
checked: !NotifServer.dnd
|
||||
icon: NotifServer.dnd ? "notifications_off" : "notifications"
|
||||
|
||||
onClicked: NotifServer.dnd = !NotifServer.dnd
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: Audio.sourceMuted ? "mic_off" : "mic"
|
||||
checked: !Audio.sourceMuted
|
||||
icon: Audio.sourceMuted ? "mic_off" : "mic"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.source?.audio;
|
||||
if ( audio )
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
icon: Audio.muted ? "volume_off" : "volume_up"
|
||||
checked: !Audio.muted
|
||||
icon: Audio.muted ? "volume_off" : "volume_up"
|
||||
|
||||
onClicked: {
|
||||
const audio = Audio.sink?.audio;
|
||||
if ( audio )
|
||||
if (audio)
|
||||
audio.muted = !audio.muted;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle {
|
||||
visible: Bluetooth.defaultAdapter ?? false
|
||||
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||
checked: Bluetooth.defaultAdapter?.enabled ?? false
|
||||
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
|
||||
visible: Bluetooth.defaultAdapter ?? false
|
||||
|
||||
onClicked: {
|
||||
// console.log(Bluetooth.defaultAdapter)
|
||||
const adapter = Bluetooth.defaultAdapter
|
||||
if ( adapter )
|
||||
const adapter = Bluetooth.defaultAdapter;
|
||||
if (adapter)
|
||||
adapter.enabled = !adapter.enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-dnd"
|
||||
@@ -86,20 +91,20 @@ CustomRect {
|
||||
}
|
||||
}
|
||||
|
||||
component Toggle: IconButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
|
||||
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
|
||||
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
toggle: true
|
||||
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
|
||||
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
component Toggle: IconButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
|
||||
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
|
||||
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
|
||||
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
toggle: true
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,26 +4,27 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var props
|
||||
required property var visibilities
|
||||
required property Item popouts
|
||||
required property Item popouts
|
||||
required property var props
|
||||
required property var visibilities
|
||||
|
||||
implicitWidth: layout.implicitWidth
|
||||
implicitHeight: layout.implicitHeight
|
||||
implicitHeight: layout.implicitHeight
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
|
||||
IdleInhibit {}
|
||||
IdleInhibit {
|
||||
}
|
||||
|
||||
Toggles {
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
Toggles {
|
||||
popouts: root.popouts
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,120 +5,115 @@ import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + 18 * 2
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + 18 * 2
|
||||
radius: 8
|
||||
|
||||
radius: 8
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
clip: true
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 18
|
||||
spacing: 10
|
||||
|
||||
CustomRect {
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: icon.implicitHeight + 7 * 2
|
||||
|
||||
radius: 1000
|
||||
color: IdleInhibitor.enabled ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3secondaryContainer
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "coffee"
|
||||
color: IdleInhibitor.enabled ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Keep Awake")
|
||||
font.pointSize: 13
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
font.pointSize: 11
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
checked: IdleInhibitor.enabled
|
||||
onToggled: IdleInhibitor.enabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: activeChip
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 20
|
||||
anchors.bottomMargin: IdleInhibitor.enabled ? 18 : -implicitHeight
|
||||
anchors.leftMargin: 18
|
||||
|
||||
opacity: IdleInhibitor.enabled ? 1 : 0
|
||||
scale: IdleInhibitor.enabled ? 1 : 0.5
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => opacity > 0)
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
implicitWidth: activeText.implicitWidth + 10 * 2
|
||||
implicitHeight: activeText.implicitHeight + 10 * 2
|
||||
|
||||
radius: 1000
|
||||
color: DynamicColors.palette.m3primary
|
||||
|
||||
CustomText {
|
||||
id: activeText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: Math.round(11 * 0.9)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
Behavior on implicitHeight {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 18
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: 10
|
||||
|
||||
CustomRect {
|
||||
color: IdleInhibitor.enabled ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3secondaryContainer
|
||||
implicitHeight: icon.implicitHeight + 7 * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: 1000
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: IdleInhibitor.enabled ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSecondaryContainer
|
||||
font.pointSize: 18
|
||||
text: "coffee"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: 13
|
||||
text: qsTr("Keep Awake")
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.fillWidth: true
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: 11
|
||||
text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
|
||||
}
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
checked: IdleInhibitor.enabled
|
||||
|
||||
onToggled: IdleInhibitor.enabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: activeChip
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: IdleInhibitor.enabled ? 18 : -implicitHeight
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 18
|
||||
anchors.topMargin: 20
|
||||
opacity: IdleInhibitor.enabled ? 1 : 0
|
||||
scale: IdleInhibitor.enabled ? 1 : 0.5
|
||||
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
sourceComponent: CustomRect {
|
||||
color: DynamicColors.palette.m3primary
|
||||
implicitHeight: activeText.implicitHeight + 10 * 2
|
||||
implicitWidth: activeText.implicitWidth + 10 * 2
|
||||
radius: 1000
|
||||
|
||||
CustomText {
|
||||
id: activeText
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.palette.m3onPrimary
|
||||
font.pointSize: Math.round(11 * 0.9)
|
||||
text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => opacity > 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,91 +6,89 @@ import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property Item sidebar
|
||||
required property Item popouts
|
||||
required property Item popouts
|
||||
readonly property PersistentProperties props: PersistentProperties {
|
||||
property string recordingConfirmDelete
|
||||
property bool recordingListExpanded: false
|
||||
property string recordingMode
|
||||
|
||||
readonly property PersistentProperties props: PersistentProperties {
|
||||
property bool recordingListExpanded: false
|
||||
property string recordingConfirmDelete
|
||||
property string recordingMode
|
||||
reloadableId: "utilities"
|
||||
}
|
||||
readonly property bool shouldBeActive: visibilities.sidebar
|
||||
required property Item sidebar
|
||||
required property var visibilities
|
||||
|
||||
reloadableId: "utilities"
|
||||
}
|
||||
readonly property bool shouldBeActive: visibilities.sidebar
|
||||
implicitHeight: 0
|
||||
implicitWidth: sidebar.visible ? sidebar.width : Config.utilities.sizes.width
|
||||
visible: height > 0
|
||||
|
||||
visible: height > 0
|
||||
implicitHeight: 0
|
||||
implicitWidth: sidebar.visible ? sidebar.width : Config.utilities.sizes.width
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeActive
|
||||
|
||||
onStateChanged: {
|
||||
if (state === "visible" && timer.running) {
|
||||
timer.triggered();
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight + 8 * 2
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeActive
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight + 8 * 2
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
]
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
onStateChanged: {
|
||||
if (state === "visible" && timer.running) {
|
||||
timer.triggered();
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
running: true
|
||||
interval: 1000
|
||||
onTriggered: {
|
||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||
content.visible = true;
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
interval: 1000
|
||||
running: true
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
onTriggered: {
|
||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
||||
content.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
visible: false
|
||||
active: true
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
sourceComponent: Content {
|
||||
implicitWidth: root.implicitWidth - 8 * 2
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
active: true
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
anchors.top: parent.top
|
||||
visible: false
|
||||
|
||||
sourceComponent: Content {
|
||||
implicitWidth: root.implicitWidth - 8 * 2
|
||||
popouts: root.popouts
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,64 +5,64 @@ import qs.Config
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property var panels
|
||||
readonly property Props props: Props {}
|
||||
required property var panels
|
||||
readonly property Props props: Props {
|
||||
}
|
||||
required property var visibilities
|
||||
|
||||
visible: width > 0
|
||||
implicitWidth: 0
|
||||
implicitWidth: 0
|
||||
visible: width > 0
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.sidebar
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.visibilities.sidebar
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Config.sidebar.sizes.width
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
root.implicitWidth: Config.sidebar.sizes.width
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitWidth"
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
property: "implicitWidth"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitWidth"
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
]
|
||||
property: "implicitWidth"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
anchors.bottomMargin: 0
|
||||
active: true
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 8
|
||||
anchors.top: parent.top
|
||||
|
||||
active: true
|
||||
Component.onCompleted: active = Qt.binding(() => (root.visibilities.sidebar && Config.sidebar.enabled) || root.visible)
|
||||
sourceComponent: Content {
|
||||
implicitWidth: Config.sidebar.sizes.width - 8 * 2
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
sourceComponent: Content {
|
||||
implicitWidth: Config.sidebar.sizes.width - 8 * 2
|
||||
props: root.props
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
Component.onCompleted: active = Qt.binding(() => (root.visibilities.sidebar && Config.sidebar.enabled) || root.visible)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,37 +3,36 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property var visibilities
|
||||
required property Item panels
|
||||
required property Item panels
|
||||
required property var visibilities
|
||||
|
||||
visible: height > 0
|
||||
implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth)
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth)
|
||||
visible: height > 0
|
||||
|
||||
states: State {
|
||||
name: "hidden"
|
||||
when: root.visibilities.sidebar
|
||||
states: State {
|
||||
name: "hidden"
|
||||
when: root.visibilities.sidebar
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: 0
|
||||
}
|
||||
}
|
||||
PropertyChanges {
|
||||
root.implicitHeight: 0
|
||||
}
|
||||
}
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
Content {
|
||||
id: content
|
||||
|
||||
Content {
|
||||
id: content
|
||||
|
||||
visibilities: root.visibilities
|
||||
panels: root.panels
|
||||
}
|
||||
panels: root.panels
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
+55
-48
@@ -4,56 +4,63 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: 10
|
||||
readonly property bool flatten: wrapper.width < rounding * 2
|
||||
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
|
||||
readonly property bool flatten: wrapper.width < rounding * 2
|
||||
readonly property real rounding: 10
|
||||
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
|
||||
required property Wrapper wrapper
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
PathArc {
|
||||
relativeX: -root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
}
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width - root.roundingX * 3)
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: -root.roundingX * 2
|
||||
relativeY: root.rounding * 2
|
||||
radiusX: Math.min(root.rounding * 2, root.wrapper.width)
|
||||
radiusY: root.rounding * 2
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.rounding * 4
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.roundingX * 2
|
||||
relativeY: root.rounding * 2
|
||||
radiusX: Math.min(root.rounding * 2, root.wrapper.width)
|
||||
radiusY: root.rounding * 2
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.roundingX * 3
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathArc {
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
relativeX: -root.roundingX
|
||||
relativeY: root.rounding
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: -(root.wrapper.width - root.roundingX * 3)
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: Math.min(root.rounding * 2, root.wrapper.width)
|
||||
radiusY: root.rounding * 2
|
||||
relativeX: -root.roundingX * 2
|
||||
relativeY: root.rounding * 2
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: root.wrapper.height - root.rounding * 4
|
||||
}
|
||||
|
||||
PathArc {
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: Math.min(root.rounding * 2, root.wrapper.width)
|
||||
radiusY: root.rounding * 2
|
||||
relativeX: root.roundingX * 2
|
||||
relativeY: root.rounding * 2
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.roundingX * 3
|
||||
relativeY: 0
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
relativeX: root.roundingX
|
||||
relativeY: root.rounding
|
||||
}
|
||||
}
|
||||
|
||||
+97
-98
@@ -8,129 +8,128 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Brightness.Monitor monitor
|
||||
required property var visibilities
|
||||
required property real brightness
|
||||
required property Brightness.Monitor monitor
|
||||
required property bool muted
|
||||
required property bool sourceMuted
|
||||
required property real sourceVolume
|
||||
required property var visibilities
|
||||
required property real volume
|
||||
|
||||
required property real volume
|
||||
required property bool muted
|
||||
required property real sourceVolume
|
||||
required property bool sourceMuted
|
||||
required property real brightness
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.small * 2
|
||||
implicitWidth: layout.implicitWidth + Appearance.padding.small * 2
|
||||
|
||||
implicitWidth: layout.implicitWidth + Appearance.padding.small * 2
|
||||
implicitHeight: layout.implicitHeight + Appearance.padding.small * 2
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
// Speaker volume
|
||||
CustomMouseArea {
|
||||
function onWheel(event: WheelEvent) {
|
||||
if (event.angleDelta.y > 0)
|
||||
Audio.incrementVolume();
|
||||
else if (event.angleDelta.y < 0)
|
||||
Audio.decrementVolume();
|
||||
}
|
||||
|
||||
// Speaker volume
|
||||
CustomMouseArea {
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
|
||||
function onWheel(event: WheelEvent) {
|
||||
if (event.angleDelta.y > 0)
|
||||
Audio.incrementVolume();
|
||||
else if (event.angleDelta.y < 0)
|
||||
Audio.decrementVolume();
|
||||
}
|
||||
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
|
||||
icon: Icons.getVolumeIcon(value, root.muted)
|
||||
value: root.volume
|
||||
to: Config.services.maxVolume
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
color: Audio.muted ? DynamicColors.palette.m3error : DynamicColors.palette.m3secondary
|
||||
onMoved: Audio.setVolume(value)
|
||||
}
|
||||
}
|
||||
icon: Icons.getVolumeIcon(value, root.muted)
|
||||
to: Config.services.maxVolume
|
||||
value: root.volume
|
||||
|
||||
// Microphone volume
|
||||
WrappedLoader {
|
||||
shouldBeActive: Config.osd.enableMicrophone && (!Config.osd.enableBrightness || !root.visibilities.session)
|
||||
onMoved: Audio.setVolume(value)
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: CustomMouseArea {
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
// Microphone volume
|
||||
WrappedLoader {
|
||||
shouldBeActive: Config.osd.enableMicrophone && (!Config.osd.enableBrightness || !root.visibilities.session)
|
||||
|
||||
function onWheel(event: WheelEvent) {
|
||||
if (event.angleDelta.y > 0)
|
||||
Audio.incrementSourceVolume();
|
||||
else if (event.angleDelta.y < 0)
|
||||
Audio.decrementSourceVolume();
|
||||
}
|
||||
sourceComponent: CustomMouseArea {
|
||||
function onWheel(event: WheelEvent) {
|
||||
if (event.angleDelta.y > 0)
|
||||
Audio.incrementSourceVolume();
|
||||
else if (event.angleDelta.y < 0)
|
||||
Audio.decrementSourceVolume();
|
||||
}
|
||||
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
|
||||
icon: Icons.getMicVolumeIcon(value, root.sourceMuted)
|
||||
value: root.sourceVolume
|
||||
to: Config.services.maxVolume
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
color: Audio.sourceMuted ? DynamicColors.palette.m3error : DynamicColors.palette.m3secondary
|
||||
onMoved: Audio.setSourceVolume(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
icon: Icons.getMicVolumeIcon(value, root.sourceMuted)
|
||||
to: Config.services.maxVolume
|
||||
value: root.sourceVolume
|
||||
|
||||
// Brightness
|
||||
WrappedLoader {
|
||||
shouldBeActive: Config.osd.enableBrightness
|
||||
onMoved: Audio.setSourceVolume(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: CustomMouseArea {
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
// Brightness
|
||||
WrappedLoader {
|
||||
shouldBeActive: Config.osd.enableBrightness
|
||||
|
||||
function onWheel(event: WheelEvent) {
|
||||
const monitor = root.monitor;
|
||||
if (!monitor)
|
||||
return;
|
||||
if (event.angleDelta.y > 0)
|
||||
monitor.setBrightness(monitor.brightness + Config.services.brightnessIncrement);
|
||||
else if (event.angleDelta.y < 0)
|
||||
monitor.setBrightness(monitor.brightness - Config.services.brightnessIncrement);
|
||||
}
|
||||
sourceComponent: CustomMouseArea {
|
||||
function onWheel(event: WheelEvent) {
|
||||
const monitor = root.monitor;
|
||||
if (!monitor)
|
||||
return;
|
||||
if (event.angleDelta.y > 0)
|
||||
monitor.setBrightness(monitor.brightness + Config.services.brightnessIncrement);
|
||||
else if (event.angleDelta.y < 0)
|
||||
monitor.setBrightness(monitor.brightness - Config.services.brightnessIncrement);
|
||||
}
|
||||
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
implicitHeight: Config.osd.sizes.sliderHeight
|
||||
implicitWidth: Config.osd.sizes.sliderWidth
|
||||
|
||||
icon: `brightness_${(Math.round(value * 6) + 1)}`
|
||||
value: root.brightness
|
||||
onMoved: {
|
||||
if ( Config.osd.allMonBrightness ) {
|
||||
root.monitor?.setBrightness(value)
|
||||
FilledSlider {
|
||||
anchors.fill: parent
|
||||
icon: `brightness_${(Math.round(value * 6) + 1)}`
|
||||
value: root.brightness
|
||||
|
||||
onMoved: {
|
||||
if (Config.osd.allMonBrightness) {
|
||||
root.monitor?.setBrightness(value);
|
||||
} else {
|
||||
for (const mon of Brightness.monitors) {
|
||||
mon.setBrightness(value)
|
||||
mon.setBrightness(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WrappedLoader: Loader {
|
||||
required property bool shouldBeActive
|
||||
component WrappedLoader: Loader {
|
||||
required property bool shouldBeActive
|
||||
|
||||
Layout.preferredHeight: shouldBeActive ? Config.osd.sizes.sliderHeight : 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
active: opacity > 0
|
||||
visible: active
|
||||
Layout.preferredHeight: shouldBeActive ? Config.osd.sizes.sliderHeight : 0
|
||||
active: opacity > 0
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
visible: active
|
||||
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
Behavior on Layout.preferredHeight {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+101
-102
@@ -8,128 +8,127 @@ import qs.Config
|
||||
import qs.Daemons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property var visibilities
|
||||
property bool hovered
|
||||
readonly property Brightness.Monitor monitor: Brightness.getMonitorForScreen(root.screen)
|
||||
readonly property bool shouldBeActive: visibilities.osd && Config.osd.enabled && !(visibilities.utilities && Config.utilities.enabled)
|
||||
property real brightness
|
||||
property bool hovered
|
||||
readonly property Brightness.Monitor monitor: Brightness.getMonitorForScreen(root.screen)
|
||||
property bool muted
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.osd && Config.osd.enabled && !(visibilities.utilities && Config.utilities.enabled)
|
||||
property bool sourceMuted
|
||||
property real sourceVolume
|
||||
required property var visibilities
|
||||
property real volume
|
||||
|
||||
property real volume
|
||||
property bool muted
|
||||
property real sourceVolume
|
||||
property bool sourceMuted
|
||||
property real brightness
|
||||
function show(): void {
|
||||
visibilities.osd = true;
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
function show(): void {
|
||||
visibilities.osd = true;
|
||||
timer.restart();
|
||||
}
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: 0
|
||||
visible: width > 0
|
||||
|
||||
Component.onCompleted: {
|
||||
volume = Audio.volume;
|
||||
muted = Audio.muted;
|
||||
sourceVolume = Audio.sourceVolume;
|
||||
sourceMuted = Audio.sourceMuted;
|
||||
brightness = root.monitor?.brightness ?? 0;
|
||||
}
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeActive
|
||||
|
||||
visible: width > 0
|
||||
implicitWidth: 0
|
||||
implicitHeight: content.implicitHeight
|
||||
PropertyChanges {
|
||||
root.implicitWidth: content.implicitWidth
|
||||
}
|
||||
}
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeActive
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitWidth"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitWidth: content.implicitWidth
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitWidth"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
Component.onCompleted: {
|
||||
volume = Audio.volume;
|
||||
muted = Audio.muted;
|
||||
sourceVolume = Audio.sourceVolume;
|
||||
sourceMuted = Audio.sourceMuted;
|
||||
brightness = root.monitor?.brightness ?? 0;
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitWidth"
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
Connections {
|
||||
function onMutedChanged(): void {
|
||||
root.show();
|
||||
root.muted = Audio.muted;
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitWidth"
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
]
|
||||
function onSourceMutedChanged(): void {
|
||||
root.show();
|
||||
root.sourceMuted = Audio.sourceMuted;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
function onSourceVolumeChanged(): void {
|
||||
root.show();
|
||||
root.sourceVolume = Audio.sourceVolume;
|
||||
}
|
||||
|
||||
function onMutedChanged(): void {
|
||||
root.show();
|
||||
root.muted = Audio.muted;
|
||||
}
|
||||
function onVolumeChanged(): void {
|
||||
root.show();
|
||||
root.volume = Audio.volume;
|
||||
}
|
||||
|
||||
function onVolumeChanged(): void {
|
||||
root.show();
|
||||
root.volume = Audio.volume;
|
||||
}
|
||||
target: Audio
|
||||
}
|
||||
|
||||
function onSourceMutedChanged(): void {
|
||||
root.show();
|
||||
root.sourceMuted = Audio.sourceMuted;
|
||||
}
|
||||
Connections {
|
||||
function onBrightnessChanged(): void {
|
||||
root.show();
|
||||
root.brightness = root.monitor?.brightness ?? 0;
|
||||
}
|
||||
|
||||
function onSourceVolumeChanged(): void {
|
||||
root.show();
|
||||
root.sourceVolume = Audio.sourceVolume;
|
||||
}
|
||||
}
|
||||
target: root.monitor
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.monitor
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
function onBrightnessChanged(): void {
|
||||
root.show();
|
||||
root.brightness = root.monitor?.brightness ?? 0;
|
||||
}
|
||||
}
|
||||
interval: Config.osd.hideDelay
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
onTriggered: {
|
||||
if (!root.hovered)
|
||||
root.visibilities.osd = false;
|
||||
}
|
||||
}
|
||||
|
||||
interval: Config.osd.hideDelay
|
||||
onTriggered: {
|
||||
if (!root.hovered)
|
||||
root.visibilities.osd = false;
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
sourceComponent: Content {
|
||||
brightness: root.brightness
|
||||
monitor: root.monitor
|
||||
muted: root.muted
|
||||
sourceMuted: root.sourceMuted
|
||||
sourceVolume: root.sourceVolume
|
||||
visibilities: root.visibilities
|
||||
volume: root.volume
|
||||
}
|
||||
|
||||
Component.onCompleted: active = Qt.binding(() => root.shouldBeActive || root.visible)
|
||||
|
||||
sourceComponent: Content {
|
||||
monitor: root.monitor
|
||||
visibilities: root.visibilities
|
||||
volume: root.volume
|
||||
muted: root.muted
|
||||
sourceVolume: root.sourceVolume
|
||||
sourceMuted: root.sourceMuted
|
||||
brightness: root.brightness
|
||||
}
|
||||
}
|
||||
Component.onCompleted: active = Qt.binding(() => root.shouldBeActive || root.visible)
|
||||
}
|
||||
}
|
||||
|
||||
+123
-116
@@ -19,53 +19,57 @@ Scope {
|
||||
PanelWindow {
|
||||
id: panelWindow
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
WlrLayershell.namespace: "ZShell-Auth"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
visible: false
|
||||
color: "transparent"
|
||||
property bool detailsOpen: false
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "ZShell-Auth"
|
||||
color: "transparent"
|
||||
visible: false
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
|
||||
onShouldShowChanged: {
|
||||
if ( root.shouldShow ) {
|
||||
panelWindow.visible = true
|
||||
openAnim.start()
|
||||
if (root.shouldShow) {
|
||||
panelWindow.visible = true;
|
||||
openAnim.start();
|
||||
} else {
|
||||
closeAnim.start()
|
||||
closeAnim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: openAnim
|
||||
target: inputPanel
|
||||
property: "opacity"
|
||||
to: 1
|
||||
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "opacity"
|
||||
target: inputPanel
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
id: closeAnim
|
||||
target: inputPanel
|
||||
property: "opacity"
|
||||
to: 0
|
||||
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
onStarted: {
|
||||
panelWindow.detailsOpen = false
|
||||
}
|
||||
property: "opacity"
|
||||
target: inputPanel
|
||||
to: 0
|
||||
|
||||
onFinished: {
|
||||
panelWindow.visible = false
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
onStarted: {
|
||||
panelWindow.detailsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// mask: Region { item: inputPanel }
|
||||
@@ -73,106 +77,105 @@ Scope {
|
||||
Rectangle {
|
||||
id: inputPanel
|
||||
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
opacity: 0
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: DynamicColors.tPalette.m3surface
|
||||
implicitHeight: layout.childrenRect.height + 28
|
||||
implicitWidth: layout.childrenRect.width + 32
|
||||
opacity: 0
|
||||
radius: 24
|
||||
|
||||
implicitWidth: layout.childrenRect.width + 32
|
||||
implicitHeight: layout.childrenRect.height + 28
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
RowLayout {
|
||||
id: contentRow
|
||||
|
||||
spacing: 24
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: icon.implicitSize
|
||||
Layout.preferredHeight: icon.implicitSize
|
||||
Layout.leftMargin: 16
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
Layout.leftMargin: 16
|
||||
Layout.preferredHeight: icon.implicitSize
|
||||
Layout.preferredWidth: icon.implicitSize
|
||||
|
||||
IconImage {
|
||||
id: icon
|
||||
|
||||
anchors.fill: parent
|
||||
visible: `${source}`.includes("://")
|
||||
|
||||
source: Quickshell.iconPath(polkitAgent.flow?.iconName, true) ?? ""
|
||||
implicitSize: 64
|
||||
mipmap: true
|
||||
source: Quickshell.iconPath(polkitAgent.flow?.iconName, true) ?? ""
|
||||
visible: `${source}`.includes("://")
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
visible: !icon.visible
|
||||
|
||||
text: "security"
|
||||
anchors.fill: parent
|
||||
font.pointSize: 64
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: "security"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: !icon.visible
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: Math.min(600, contentWidth)
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Layout.preferredWidth: Math.min(600, contentWidth)
|
||||
font.bold: true
|
||||
font.pointSize: 16
|
||||
text: polkitAgent.flow?.message
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 16
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.preferredWidth: Math.min(600, contentWidth)
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
text: polkitAgent.flow?.supplementaryMessage || "No Additional Information"
|
||||
Layout.preferredWidth: Math.min(600, contentWidth)
|
||||
color: DynamicColors.tPalette.m3onSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 12
|
||||
font.bold: true
|
||||
font.pointSize: 12
|
||||
text: polkitAgent.flow?.supplementaryMessage || "No Additional Information"
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passInput
|
||||
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: contentColumn.implicitWidth
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
echoMode: polkitAgent.flow?.responseVisible ? TextInput.Normal : TextInput.Password
|
||||
placeholderText: polkitAgent.flow?.failed ? " Incorrect Password" : " Input Password"
|
||||
selectByMouse: true
|
||||
onAccepted: okButton.clicked()
|
||||
|
||||
color: DynamicColors.palette.m3onSurfaceVariant
|
||||
placeholderTextColor: polkitAgent.flow?.failed ? DynamicColors.palette.m3onError : DynamicColors.tPalette.m3onSurfaceVariant
|
||||
|
||||
Layout.preferredWidth: contentColumn.implicitWidth
|
||||
Layout.preferredHeight: 40
|
||||
selectByMouse: true
|
||||
|
||||
background: CustomRect {
|
||||
radius: 8
|
||||
color: (polkitAgent.flow?.failed && passInput.text === "") ? DynamicColors.palette.m3error : DynamicColors.tPalette.m3surfaceVariant
|
||||
implicitHeight: 40
|
||||
color: ( polkitAgent.flow?.failed && passInput.text === "" ) ? DynamicColors.palette.m3error : DynamicColors.tPalette.m3surfaceVariant
|
||||
radius: 8
|
||||
}
|
||||
|
||||
onAccepted: okButton.clicked()
|
||||
}
|
||||
|
||||
CustomCheckbox {
|
||||
id: showPassCheckbox
|
||||
text: "Show Password"
|
||||
checked: polkitAgent.flow?.responseVisible
|
||||
onCheckedChanged: {
|
||||
passInput.echoMode = checked ? TextInput.Normal : TextInput.Password
|
||||
passInput.forceActiveFocus()
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
checked: polkitAgent.flow?.responseVisible
|
||||
text: "Show Password"
|
||||
|
||||
onCheckedChanged: {
|
||||
passInput.echoMode = checked ? TextInput.Normal : TextInput.Password;
|
||||
passInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,51 +183,49 @@ Scope {
|
||||
CustomRect {
|
||||
id: detailsPanel
|
||||
|
||||
visible: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLow
|
||||
radius: 16
|
||||
clip: true
|
||||
implicitHeight: 0
|
||||
property bool open: panelWindow.detailsOpen
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: implicitHeight
|
||||
|
||||
property bool open: panelWindow.detailsOpen
|
||||
clip: true
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLow
|
||||
implicitHeight: 0
|
||||
radius: 16
|
||||
visible: true
|
||||
|
||||
Behavior on open {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: detailsPanel
|
||||
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "implicitHeight"
|
||||
target: detailsPanel
|
||||
to: !detailsPanel.open ? textDetailsColumn.childrenRect.height + 16 : 0
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: textDetailsColumn
|
||||
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "opacity"
|
||||
target: textDetailsColumn
|
||||
to: !detailsPanel.open ? 1 : 0
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
}
|
||||
|
||||
Anim {
|
||||
target: textDetailsColumn
|
||||
|
||||
property: "scale"
|
||||
to: !detailsPanel.open ? 1 : 0.9
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
property: "scale"
|
||||
target: textDetailsColumn
|
||||
to: !detailsPanel.open ? 1 : 0.9
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: textDetailsColumn
|
||||
spacing: 8
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
opacity: 0
|
||||
scale: 0.9
|
||||
spacing: 8
|
||||
|
||||
CustomText {
|
||||
text: `actionId: ${polkitAgent.flow?.actionId}`
|
||||
@@ -239,64 +240,69 @@ Scope {
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.preferredWidth: contentRow.implicitWidth
|
||||
spacing: 8
|
||||
|
||||
CustomButton {
|
||||
id: detailsButton
|
||||
text: "Details"
|
||||
textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 92
|
||||
bgColor: DynamicColors.palette.m3surfaceContainer
|
||||
enabled: true
|
||||
radius: 1000
|
||||
|
||||
Layout.preferredWidth: 92
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: "Details"
|
||||
textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
onClicked: {
|
||||
panelWindow.detailsOpen = !panelWindow.detailsOpen
|
||||
console.log(panelWindow.detailsOpen)
|
||||
panelWindow.detailsOpen = !panelWindow.detailsOpen;
|
||||
console.log(panelWindow.detailsOpen);
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: spacer
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
id: okButton
|
||||
text: "OK"
|
||||
textColor: DynamicColors.palette.m3onPrimary
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 76
|
||||
bgColor: DynamicColors.palette.m3primary
|
||||
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
||||
radius: 1000
|
||||
Layout.preferredWidth: 76
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: "OK"
|
||||
textColor: DynamicColors.palette.m3onPrimary
|
||||
|
||||
onClicked: {
|
||||
polkitAgent.flow.submit(passInput.text)
|
||||
passInput.text = ""
|
||||
passInput.forceActiveFocus()
|
||||
polkitAgent.flow.submit(passInput.text);
|
||||
passInput.text = "";
|
||||
passInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
CustomButton {
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.preferredHeight: 40
|
||||
Layout.preferredWidth: 76
|
||||
bgColor: DynamicColors.palette.m3surfaceContainer
|
||||
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
||||
radius: 1000
|
||||
Layout.preferredWidth: 76
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: "Cancel"
|
||||
textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
onClicked: {
|
||||
root.shouldShow = false
|
||||
console.log(icon.source, icon.visible)
|
||||
polkitAgent.flow.cancelAuthenticationRequest()
|
||||
passInput.text = ""
|
||||
root.shouldShow = false;
|
||||
console.log(icon.source, icon.visible);
|
||||
polkitAgent.flow.cancelAuthenticationRequest();
|
||||
passInput.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,48 +310,49 @@ Scope {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: polkitAgent.flow
|
||||
|
||||
function onIsResponseRequiredChanged() {
|
||||
passInput.text = ""
|
||||
if ( polkitAgent.flow?.isResponseRequired )
|
||||
root.shouldShow = true
|
||||
passInput.forceActiveFocus()
|
||||
passInput.text = "";
|
||||
if (polkitAgent.flow?.isResponseRequired)
|
||||
root.shouldShow = true;
|
||||
passInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onIsSuccessfulChanged() {
|
||||
if ( polkitAgent.flow?.isSuccessful )
|
||||
root.shouldShow = false
|
||||
passInput.text = ""
|
||||
if (polkitAgent.flow?.isSuccessful)
|
||||
root.shouldShow = false;
|
||||
passInput.text = "";
|
||||
}
|
||||
|
||||
target: polkitAgent.flow
|
||||
}
|
||||
}
|
||||
|
||||
PolkitAgent {
|
||||
id: polkitAgent
|
||||
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
|
||||
required property var modelData
|
||||
|
||||
color: root.shouldShow ? "#80000000" : "transparent"
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
screen: modelData
|
||||
visible: panelWindow.visible
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
bottom: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+93
-87
@@ -5,106 +5,112 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property double percentage
|
||||
property int warningThreshold: 100
|
||||
property bool shown: true
|
||||
clip: true
|
||||
visible: width > 0 && height > 0
|
||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
||||
implicitHeight: 22
|
||||
property bool warning: percentage * 100 >= warningThreshold
|
||||
id: root
|
||||
|
||||
property color borderColor: warning ? DynamicColors.palette.m3onError : mainColor
|
||||
required property color mainColor
|
||||
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||
property color borderColor: warning ? DynamicColors.palette.m3onError : mainColor
|
||||
required property double percentage
|
||||
property bool shown: true
|
||||
property color usageColor: warning ? DynamicColors.palette.m3error : mainColor
|
||||
property bool warning: percentage * 100 >= warningThreshold
|
||||
property int warningThreshold: 100
|
||||
|
||||
Behavior on percentage {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
clip: true
|
||||
implicitHeight: 22
|
||||
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
RowLayout {
|
||||
id: resourceRowLayout
|
||||
spacing: 2
|
||||
x: shown ? 0 : -resourceRowLayout.width
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Behavior on percentage {
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitWidth: 14
|
||||
implicitHeight: root.implicitHeight
|
||||
RowLayout {
|
||||
id: resourceRowLayout
|
||||
|
||||
Rectangle {
|
||||
id: backgroundCircle
|
||||
anchors.centerIn: parent
|
||||
width: 14
|
||||
height: 14
|
||||
radius: height / 2
|
||||
color: "#40000000"
|
||||
border.color: "#404040"
|
||||
border.width: 1
|
||||
}
|
||||
spacing: 2
|
||||
x: shown ? 0 : -resourceRowLayout.width
|
||||
|
||||
Shape {
|
||||
anchors.fill: backgroundCircle
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
smooth: true
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: root.implicitHeight
|
||||
implicitWidth: 14
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: 0
|
||||
fillColor: root.usageColor
|
||||
startX: backgroundCircle.width / 2
|
||||
startY: backgroundCircle.height / 2
|
||||
Rectangle {
|
||||
id: backgroundCircle
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
border.color: "#404040"
|
||||
border.width: 1
|
||||
color: "#40000000"
|
||||
height: 14
|
||||
radius: height / 2
|
||||
width: 14
|
||||
}
|
||||
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: 0 + ( 1 / 2 )
|
||||
}
|
||||
Shape {
|
||||
anchors.fill: backgroundCircle
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
smooth: true
|
||||
|
||||
PathAngleArc {
|
||||
centerX: backgroundCircle.width / 2
|
||||
centerY: backgroundCircle.height / 2
|
||||
radiusX: backgroundCircle.width / 2 - ( 1 / 2 )
|
||||
radiusY: backgroundCircle.height / 2 - ( 1 / 2 )
|
||||
startAngle: -90
|
||||
sweepAngle: 360 * root.percentage
|
||||
}
|
||||
ShapePath {
|
||||
fillColor: root.usageColor
|
||||
startX: backgroundCircle.width / 2
|
||||
startY: backgroundCircle.height / 2
|
||||
strokeWidth: 0
|
||||
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: backgroundCircle.height / 2
|
||||
}
|
||||
}
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: 1
|
||||
strokeColor: root.borderColor
|
||||
fillColor: "transparent"
|
||||
capStyle: ShapePath.FlatCap
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: 0 + (1 / 2)
|
||||
}
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {}
|
||||
}
|
||||
PathAngleArc {
|
||||
centerX: backgroundCircle.width / 2
|
||||
centerY: backgroundCircle.height / 2
|
||||
radiusX: backgroundCircle.width / 2 - (1 / 2)
|
||||
radiusY: backgroundCircle.height / 2 - (1 / 2)
|
||||
startAngle: -90
|
||||
sweepAngle: 360 * root.percentage
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: backgroundCircle.width / 2
|
||||
centerY: backgroundCircle.height / 2
|
||||
radiusX: backgroundCircle.width / 2 - ( 1 / 2 )
|
||||
radiusY: backgroundCircle.height / 2 - ( 1 / 2 )
|
||||
startAngle: -90
|
||||
sweepAngle: 360 * root.percentage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PathLine {
|
||||
x: backgroundCircle.width / 2
|
||||
y: backgroundCircle.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
ShapePath {
|
||||
capStyle: ShapePath.FlatCap
|
||||
fillColor: "transparent"
|
||||
strokeColor: root.borderColor
|
||||
strokeWidth: 1
|
||||
|
||||
Behavior on strokeColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathAngleArc {
|
||||
centerX: backgroundCircle.width / 2
|
||||
centerY: backgroundCircle.height / 2
|
||||
radiusX: backgroundCircle.width / 2 - (1 / 2)
|
||||
radiusY: backgroundCircle.height / 2 - (1 / 2)
|
||||
startAngle: -90
|
||||
sweepAngle: 360 * root.percentage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+60
-58
@@ -5,71 +5,73 @@ import qs.Config
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property string resourceName
|
||||
required property double percentage
|
||||
required property int warningThreshold
|
||||
required property string details
|
||||
required property string iconString
|
||||
property color barColor: DynamicColors.palette.m3primary
|
||||
property color warningBarColor: DynamicColors.palette.m3error
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
id: root
|
||||
|
||||
Layout.preferredWidth: 158
|
||||
Layout.preferredHeight: columnLayout.implicitHeight
|
||||
property color barColor: DynamicColors.palette.m3primary
|
||||
required property string details
|
||||
required property string iconString
|
||||
required property double percentage
|
||||
required property string resourceName
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
property color warningBarColor: DynamicColors.palette.m3error
|
||||
required property int warningThreshold
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
Layout.preferredHeight: columnLayout.implicitHeight
|
||||
Layout.preferredWidth: 158
|
||||
|
||||
Row {
|
||||
spacing: 6
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
MaterialIcon {
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: 28
|
||||
text: root.iconString
|
||||
color: root.textColor
|
||||
}
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
|
||||
CustomText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.resourceName
|
||||
font.pointSize: 12
|
||||
color: root.textColor
|
||||
}
|
||||
}
|
||||
Row {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 6
|
||||
radius: height / 2
|
||||
color: "#40000000"
|
||||
MaterialIcon {
|
||||
color: root.textColor
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: 28
|
||||
text: root.iconString
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * Math.min(root.percentage, 1)
|
||||
height: parent.height
|
||||
radius: height / 2
|
||||
color: root.percentage * 100 >= root.warningThreshold ? root.warningBarColor : root.barColor
|
||||
CustomText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.textColor
|
||||
font.pointSize: 12
|
||||
text: root.resourceName
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 6
|
||||
color: "#40000000"
|
||||
radius: height / 2
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: root.details
|
||||
font.pointSize: 10
|
||||
color: root.textColor
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
color: root.percentage * 100 >= root.warningThreshold ? root.warningBarColor : root.barColor
|
||||
height: parent.height
|
||||
radius: height / 2
|
||||
width: parent.width * Math.min(root.percentage, 1)
|
||||
|
||||
Behavior on width {
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: root.textColor
|
||||
font.pointSize: 10
|
||||
text: root.details
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-47
@@ -5,57 +5,55 @@ import QtQuick.Layouts
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: popoutWindow
|
||||
implicitWidth: contentColumn.implicitWidth + 10 * 2
|
||||
implicitHeight: contentColumn.implicitHeight + 10
|
||||
required property var wrapper
|
||||
id: popoutWindow
|
||||
|
||||
// ShadowRect {
|
||||
// anchors.fill: contentRect
|
||||
// radius: 8
|
||||
// }
|
||||
required property var wrapper
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 10
|
||||
implicitHeight: contentColumn.implicitHeight + 10
|
||||
implicitWidth: contentColumn.implicitWidth + 10 * 2
|
||||
|
||||
ResourceDetail {
|
||||
resourceName: qsTr( "Memory Usage" )
|
||||
iconString: "\uf7a3"
|
||||
percentage: ResourceUsage.memoryUsedPercentage
|
||||
warningThreshold: 95
|
||||
details: qsTr( "%1 of %2 MB used" )
|
||||
.arg( Math.round( ResourceUsage.memoryUsed * 0.001 ))
|
||||
.arg( Math.round( ResourceUsage.memoryTotal * 0.001 ))
|
||||
}
|
||||
// ShadowRect {
|
||||
// anchors.fill: contentRect
|
||||
// radius: 8
|
||||
// }
|
||||
|
||||
ResourceDetail {
|
||||
resourceName: qsTr( "CPU Usage" )
|
||||
iconString: "\ue322"
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
warningThreshold: 95
|
||||
details: qsTr( "%1% used" )
|
||||
.arg( Math.round( ResourceUsage.cpuUsage * 100 ))
|
||||
}
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
|
||||
ResourceDetail {
|
||||
resourceName: qsTr( "GPU Usage" )
|
||||
iconString: "\ue30f"
|
||||
percentage: ResourceUsage.gpuUsage
|
||||
warningThreshold: 95
|
||||
details: qsTr( "%1% used" )
|
||||
.arg( Math.round( ResourceUsage.gpuUsage * 100 ))
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
spacing: 10
|
||||
|
||||
ResourceDetail {
|
||||
resourceName: qsTr( "VRAM Usage" )
|
||||
iconString: "\ue30d"
|
||||
percentage: ResourceUsage.gpuMemUsage
|
||||
warningThreshold: 95
|
||||
details: qsTr( "%1% used" )
|
||||
.arg( Math.round( ResourceUsage.gpuMemUsage * 100 ))
|
||||
}
|
||||
}
|
||||
ResourceDetail {
|
||||
details: qsTr("%1 of %2 MB used").arg(Math.round(ResourceUsage.memoryUsed * 0.001)).arg(Math.round(ResourceUsage.memoryTotal * 0.001))
|
||||
iconString: "\uf7a3"
|
||||
percentage: ResourceUsage.memoryUsedPercentage
|
||||
resourceName: qsTr("Memory Usage")
|
||||
warningThreshold: 95
|
||||
}
|
||||
|
||||
ResourceDetail {
|
||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.cpuUsage * 100))
|
||||
iconString: "\ue322"
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
resourceName: qsTr("CPU Usage")
|
||||
warningThreshold: 95
|
||||
}
|
||||
|
||||
ResourceDetail {
|
||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.gpuUsage * 100))
|
||||
iconString: "\ue30f"
|
||||
percentage: ResourceUsage.gpuUsage
|
||||
resourceName: qsTr("GPU Usage")
|
||||
warningThreshold: 95
|
||||
}
|
||||
|
||||
ResourceDetail {
|
||||
details: qsTr("%1% used").arg(Math.round(ResourceUsage.gpuMemUsage * 100))
|
||||
iconString: "\ue30d"
|
||||
percentage: ResourceUsage.gpuMemUsage
|
||||
resourceName: qsTr("VRAM Usage")
|
||||
warningThreshold: 95
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+94
-75
@@ -7,98 +7,117 @@ import Quickshell.Io
|
||||
import qs.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property double memoryTotal: 1
|
||||
id: root
|
||||
|
||||
property string autoGpuType: "NONE"
|
||||
property double cpuUsage: 0
|
||||
property double gpuMemUsage: 0
|
||||
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
||||
property double gpuUsage: 0
|
||||
property double memoryFree: 1
|
||||
property double memoryTotal: 1
|
||||
property double memoryUsed: memoryTotal - memoryFree
|
||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
||||
property double swapTotal: 1
|
||||
property double memoryUsedPercentage: memoryUsed / memoryTotal
|
||||
property var previousCpuStats
|
||||
property double swapFree: 1
|
||||
property double swapTotal: 1
|
||||
property double swapUsed: swapTotal - swapFree
|
||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||
property double cpuUsage: 0
|
||||
property var previousCpuStats
|
||||
property double gpuUsage: 0
|
||||
property double gpuMemUsage: 0
|
||||
property double totalMem: 0
|
||||
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
||||
property string autoGpuType: "NONE"
|
||||
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
|
||||
property double totalMem: 0
|
||||
|
||||
Timer {
|
||||
interval: 1
|
||||
running: true
|
||||
repeat: true
|
||||
repeat: true
|
||||
running: true
|
||||
|
||||
onTriggered: {
|
||||
// Reload files
|
||||
fileMeminfo.reload()
|
||||
fileStat.reload()
|
||||
// Reload files
|
||||
fileMeminfo.reload();
|
||||
fileStat.reload();
|
||||
|
||||
// Parse memory and swap usage
|
||||
const textMeminfo = fileMeminfo.text()
|
||||
memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1)
|
||||
memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0)
|
||||
swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1)
|
||||
swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0)
|
||||
// Parse memory and swap usage
|
||||
const textMeminfo = fileMeminfo.text();
|
||||
memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1);
|
||||
memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0);
|
||||
swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1);
|
||||
swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0);
|
||||
|
||||
// Parse CPU usage
|
||||
const textStat = fileStat.text()
|
||||
const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/)
|
||||
if (cpuLine) {
|
||||
const stats = cpuLine.slice(1).map(Number)
|
||||
const total = stats.reduce((a, b) => a + b, 0)
|
||||
const idle = stats[3]
|
||||
// Parse CPU usage
|
||||
const textStat = fileStat.text();
|
||||
const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
|
||||
if (cpuLine) {
|
||||
const stats = cpuLine.slice(1).map(Number);
|
||||
const total = stats.reduce((a, b) => a + b, 0);
|
||||
const idle = stats[3];
|
||||
|
||||
if (previousCpuStats) {
|
||||
const totalDiff = total - previousCpuStats.total
|
||||
const idleDiff = idle - previousCpuStats.idle
|
||||
cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0
|
||||
}
|
||||
if (previousCpuStats) {
|
||||
const totalDiff = total - previousCpuStats.total;
|
||||
const idleDiff = idle - previousCpuStats.idle;
|
||||
cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
|
||||
}
|
||||
|
||||
previousCpuStats = { total, idle }
|
||||
}
|
||||
if ( root.gpuType === "NVIDIA" ) {
|
||||
processGpu.running = true
|
||||
}
|
||||
previousCpuStats = {
|
||||
total,
|
||||
idle
|
||||
};
|
||||
}
|
||||
if (root.gpuType === "NVIDIA") {
|
||||
processGpu.running = true;
|
||||
}
|
||||
|
||||
interval = 3000
|
||||
}
|
||||
interval = 3000;
|
||||
}
|
||||
}
|
||||
|
||||
FileView { id: fileMeminfo; path: "/proc/meminfo" }
|
||||
FileView { id: fileStat; path: "/proc/stat" }
|
||||
FileView {
|
||||
id: fileMeminfo
|
||||
|
||||
Process {
|
||||
id: gpuTypeCheck
|
||||
path: "/proc/meminfo"
|
||||
}
|
||||
|
||||
running: !Config.services.gpuType
|
||||
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"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: root.autoGpuType = text.trim()
|
||||
}
|
||||
}
|
||||
FileView {
|
||||
id: fileStat
|
||||
|
||||
Process {
|
||||
id: oneshotMem
|
||||
command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"]
|
||||
running: root.gpuType === "NVIDIA" && totalMem === 0
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
totalMem = Number(this.text.trim())
|
||||
oneshotMem.running = false
|
||||
}
|
||||
}
|
||||
}
|
||||
path: "/proc/stat"
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processGpu
|
||||
command: ["nvidia-smi", "--query-gpu=utilization.gpu,memory.used", "--format=csv,noheader,nounits"]
|
||||
running: false
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const parts = this.text.trim().split(", ")
|
||||
gpuUsage = Number(parts[0]) / 100
|
||||
gpuMemUsage = Number(parts[1]) / totalMem
|
||||
}
|
||||
}
|
||||
}
|
||||
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" && totalMem === 0
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
totalMem = Number(this.text.trim());
|
||||
oneshotMem.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: processGpu
|
||||
|
||||
command: ["nvidia-smi", "--query-gpu=utilization.gpu,memory.used", "--format=csv,noheader,nounits"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const parts = this.text.trim().split(", ");
|
||||
gpuUsage = Number(parts[0]) / 100;
|
||||
gpuMemUsage = Number(parts[1]) / totalMem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+73
-67
@@ -10,81 +10,87 @@ import qs.Effects
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||
implicitHeight: 34
|
||||
clip: true
|
||||
id: root
|
||||
|
||||
Rectangle {
|
||||
id: backgroundRect
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
implicitHeight: 22
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: height / 2
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
clip: true
|
||||
implicitHeight: 34
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||
|
||||
spacing: 6
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 5
|
||||
anchors.rightMargin: 5
|
||||
Rectangle {
|
||||
id: backgroundRect
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: "memory_alt"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
}
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 22
|
||||
radius: height / 2
|
||||
|
||||
Resource {
|
||||
percentage: ResourceUsage.memoryUsedPercentage
|
||||
warningThreshold: 95
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
}
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: "memory"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
}
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Resource {
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
warningThreshold: 80
|
||||
mainColor: DynamicColors.palette.m3secondary
|
||||
}
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: "gamepad"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
}
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 5
|
||||
anchors.rightMargin: 5
|
||||
spacing: 6
|
||||
|
||||
Resource {
|
||||
percentage: ResourceUsage.gpuUsage
|
||||
mainColor: DynamicColors.palette.m3tertiary
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pixelSize: 18
|
||||
text: "memory_alt"
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.pixelSize: 18
|
||||
text: "developer_board"
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
}
|
||||
Resource {
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
percentage: ResourceUsage.memoryUsedPercentage
|
||||
warningThreshold: 95
|
||||
}
|
||||
|
||||
Resource {
|
||||
percentage: ResourceUsage.gpuMemUsage
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
}
|
||||
}
|
||||
}
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pixelSize: 18
|
||||
text: "memory"
|
||||
}
|
||||
|
||||
Resource {
|
||||
mainColor: DynamicColors.palette.m3secondary
|
||||
percentage: ResourceUsage.cpuUsage
|
||||
warningThreshold: 80
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pixelSize: 18
|
||||
text: "gamepad"
|
||||
}
|
||||
|
||||
Resource {
|
||||
mainColor: DynamicColors.palette.m3tertiary
|
||||
percentage: ResourceUsage.gpuUsage
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
font.pixelSize: 18
|
||||
text: "developer_board"
|
||||
}
|
||||
|
||||
Resource {
|
||||
mainColor: DynamicColors.palette.m3primary
|
||||
percentage: ResourceUsage.gpuMemUsage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,28 @@ import QtQuick
|
||||
import QtQuick.Shapes
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: 8
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
|
||||
strokeWidth: -1
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real rounding: 8
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
required property Wrapper wrapper
|
||||
|
||||
fillColor: DynamicColors.palette.m3surface
|
||||
strokeWidth: -1
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {
|
||||
}
|
||||
}
|
||||
|
||||
PathArc {
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.roundingY, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min( root.roundingY, root.wrapper.height )
|
||||
}
|
||||
|
||||
PathLine {
|
||||
@@ -28,11 +32,11 @@ ShapePath {
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min( root.rounding, root.wrapper.height )
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
@@ -41,26 +45,22 @@ ShapePath {
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: - root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min( root.rounding, root.wrapper.height )
|
||||
direction: PathArc.Counterclockwise
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
|
||||
PathLine {
|
||||
relativeX: 0
|
||||
relativeY: - ( root.wrapper.height - root.roundingY * 2 )
|
||||
relativeY: -(root.wrapper.height - root.roundingY * 2)
|
||||
}
|
||||
|
||||
PathArc {
|
||||
relativeX: root.rounding
|
||||
relativeY: - root.roundingY
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min( root.rounding, root.wrapper.height )
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
CAnim {}
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
relativeX: root.rounding
|
||||
relativeY: -root.roundingY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,105 +12,105 @@ import qs.Helpers
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item content
|
||||
|
||||
implicitHeight: clayout.contentHeight + Appearance.padding.smaller * 2
|
||||
implicitWidth: clayout.contentWidth + Appearance.padding.smaller * 2
|
||||
|
||||
ListModel {
|
||||
id: listModel
|
||||
|
||||
ListElement {
|
||||
name: "General"
|
||||
icon: "settings"
|
||||
name: "General"
|
||||
}
|
||||
|
||||
|
||||
ListElement {
|
||||
name: "Wallpaper"
|
||||
icon: "wallpaper"
|
||||
name: "Wallpaper"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Bar"
|
||||
icon: "settop_component"
|
||||
name: "Bar"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Lockscreen"
|
||||
icon: "lock"
|
||||
name: "Lockscreen"
|
||||
}
|
||||
|
||||
|
||||
ListElement {
|
||||
name: "Services"
|
||||
icon: "build_circle"
|
||||
name: "Services"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Notifications"
|
||||
icon: "notifications"
|
||||
name: "Notifications"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Sidebar"
|
||||
icon: "view_sidebar"
|
||||
name: "Sidebar"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Utilities"
|
||||
icon: "handyman"
|
||||
name: "Utilities"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Dashboard"
|
||||
icon: "dashboard"
|
||||
name: "Dashboard"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
icon: "colors"
|
||||
name: "Appearance"
|
||||
icon: "colors"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "On screen display"
|
||||
icon: "display_settings"
|
||||
name: "On screen display"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Launcher"
|
||||
icon: "rocket_launch"
|
||||
name: "Launcher"
|
||||
}
|
||||
|
||||
ListElement {
|
||||
name: "Colors"
|
||||
icon: "colors"
|
||||
name: "Colors"
|
||||
}
|
||||
}
|
||||
|
||||
required property Item content
|
||||
|
||||
implicitWidth: clayout.contentWidth + Appearance.padding.smaller * 2
|
||||
implicitHeight: clayout.contentHeight + Appearance.padding.smaller * 2
|
||||
|
||||
CustomRect {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 4
|
||||
|
||||
CustomListView {
|
||||
id: clayout
|
||||
|
||||
anchors.centerIn: parent
|
||||
model: listModel
|
||||
|
||||
contentWidth: contentItem.childrenRect.width
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
implicitWidth: contentItem.childrenRect.width
|
||||
contentWidth: contentItem.childrenRect.width
|
||||
highlightFollowsCurrentItem: false
|
||||
implicitHeight: contentItem.childrenRect.height
|
||||
|
||||
implicitWidth: contentItem.childrenRect.width
|
||||
model: listModel
|
||||
spacing: 5
|
||||
|
||||
delegate: Category {
|
||||
}
|
||||
highlight: CustomRect {
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 4
|
||||
|
||||
y: clayout.currentItem?.y ?? 0
|
||||
implicitWidth: clayout.width
|
||||
implicitHeight: clayout.currentItem?.implicitHeight ?? 0
|
||||
implicitWidth: clayout.width
|
||||
radius: 4
|
||||
y: clayout.currentItem?.y ?? 0
|
||||
|
||||
Behavior on y {
|
||||
Anim {
|
||||
@@ -119,53 +119,49 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
|
||||
delegate: Category {}
|
||||
}
|
||||
}
|
||||
|
||||
component Category: CustomRect {
|
||||
id: categoryItem
|
||||
|
||||
required property string name
|
||||
required property string icon
|
||||
required property int index
|
||||
required property string name
|
||||
|
||||
implicitWidth: 200
|
||||
implicitHeight: 42
|
||||
implicitWidth: 200
|
||||
radius: 4
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
text: categoryItem.icon
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: icon.contentWidth
|
||||
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
font.pointSize: 22
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.preferredWidth: icon.contentWidth
|
||||
Layout.fillHeight: true
|
||||
text: categoryItem.icon
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
text: categoryItem.name
|
||||
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.spacing.normal
|
||||
color: categoryItem.index === clayout.currentIndex ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
text: categoryItem.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,21 @@ CustomRect {
|
||||
|
||||
ColumnLayout {
|
||||
id: clayout
|
||||
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
CustomRect {
|
||||
|
||||
Layout.preferredHeight: colorLayout.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.preferredHeight: colorLayout.implicitHeight
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: colorLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.right: parent.right
|
||||
|
||||
Settings {
|
||||
name: "smth"
|
||||
@@ -49,20 +47,19 @@ CustomRect {
|
||||
|
||||
required property string name
|
||||
|
||||
Layout.preferredWidth: 200
|
||||
Layout.preferredHeight: 42
|
||||
Layout.preferredWidth: 200
|
||||
radius: 4
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
text: settingsItem.name
|
||||
font.pointSize: 32
|
||||
anchors.right: parent.right
|
||||
font.bold: true
|
||||
font.pointSize: 32
|
||||
text: settingsItem.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@ import qs.Helpers
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ CustomRect {
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,26 +28,26 @@ CustomRect {
|
||||
|
||||
required property string name
|
||||
|
||||
implicitWidth: 200
|
||||
implicitHeight: 42
|
||||
implicitWidth: 200
|
||||
radius: 4
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
text: settingsItem.name
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Appearance.spacing.normal
|
||||
text: settingsItem.name
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,71 +11,69 @@ import qs.Helpers
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
readonly property real nonAnimWidth: view.implicitWidth + 500 + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
||||
property string currentCategory: "general"
|
||||
readonly property real nonAnimHeight: view.implicitHeight + viewWrapper.anchors.margins * 2
|
||||
readonly property real nonAnimWidth: view.implicitWidth + 500 + viewWrapper.anchors.margins * 2
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
|
||||
function onCurrentCategoryChanged() {
|
||||
stack.pop();
|
||||
if ( currentCategory === "general" ) {
|
||||
if (currentCategory === "general") {
|
||||
stack.push(general);
|
||||
} else if ( currentCategory === "wallpaper" ) {
|
||||
} else if (currentCategory === "wallpaper") {
|
||||
stack.push(background);
|
||||
} else if ( currentCategory === "appearance" ) {
|
||||
} else if (currentCategory === "appearance") {
|
||||
stack.push(appearance);
|
||||
}
|
||||
}
|
||||
|
||||
target: root
|
||||
}
|
||||
|
||||
ClippingRectangle {
|
||||
id: viewWrapper
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
color: "transparent"
|
||||
|
||||
Item {
|
||||
id: view
|
||||
anchors.top: parent.top
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
implicitWidth: layout.implicitWidth
|
||||
anchors.top: parent.top
|
||||
implicitHeight: layout.implicitHeight
|
||||
|
||||
implicitWidth: layout.implicitWidth
|
||||
|
||||
Categories {
|
||||
id: layout
|
||||
|
||||
content: root
|
||||
|
||||
anchors.fill: parent
|
||||
content: root
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: categoryContent
|
||||
|
||||
anchors.top: parent.top
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.left: view.right
|
||||
anchors.leftMargin: Appearance.spacing.smaller
|
||||
|
||||
radius: 4
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 4
|
||||
|
||||
StackView {
|
||||
id: stack
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.smaller
|
||||
|
||||
initialItem: general
|
||||
}
|
||||
}
|
||||
@@ -84,18 +82,21 @@ Item {
|
||||
Component {
|
||||
id: general
|
||||
|
||||
Cat.General {}
|
||||
Cat.General {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: background
|
||||
|
||||
Cat.Background {}
|
||||
Cat.Background {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: appearance
|
||||
|
||||
Cat.Appearance {}
|
||||
Cat.Appearance {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Modules as Modules
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
required property string name
|
||||
required property var object
|
||||
required property string setting
|
||||
required property string name
|
||||
|
||||
Layout.preferredHeight: 42
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.preferredHeight: 42
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
text: root.name
|
||||
font.pointSize: 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
Layout.fillWidth: true
|
||||
font.pointSize: 16
|
||||
text: root.name
|
||||
}
|
||||
|
||||
CustomSwitch {
|
||||
id: cswitch
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
checked: root.object[root.setting]
|
||||
|
||||
onToggled: {
|
||||
root.object[root.setting] = checked;
|
||||
Config.save();
|
||||
|
||||
@@ -8,5 +8,4 @@ import qs.Config
|
||||
import qs.Helpers
|
||||
|
||||
Item {
|
||||
|
||||
}
|
||||
|
||||
@@ -7,56 +7,52 @@ import qs.Helpers
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property PersistentProperties visibilities
|
||||
required property var panels
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: 0
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
visible: height > 0
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
|
||||
when: root.visibilities.settings
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: content.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
},
|
||||
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: root
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
visible: true
|
||||
active: true
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: true
|
||||
|
||||
sourceComponent: Content {
|
||||
visibilities: root.visibilities
|
||||
|
||||
+19
-19
@@ -7,29 +7,29 @@ import qs.Helpers
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
property bool launcherInterrupted
|
||||
readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false
|
||||
property bool launcherInterrupted
|
||||
|
||||
CustomShortcut {
|
||||
description: "Toggle launcher"
|
||||
name: "toggle-launcher"
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-launcher"
|
||||
description: "Toggle launcher"
|
||||
onPressed: root.launcherInterrupted = false
|
||||
onReleased: {
|
||||
if (!root.launcherInterrupted && !root.hasFullscreen) {
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.launcher = !visibilities.launcher;
|
||||
}
|
||||
root.launcherInterrupted = false;
|
||||
}
|
||||
}
|
||||
onPressed: root.launcherInterrupted = false
|
||||
onReleased: {
|
||||
if (!root.launcherInterrupted && !root.hasFullscreen) {
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.launcher = !visibilities.launcher;
|
||||
}
|
||||
root.launcherInterrupted = false;
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-nc"
|
||||
|
||||
onPressed: {
|
||||
const visibilities = Visibilities.getForActive()
|
||||
visibilities.sidebar = !visibilities.sidebar
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.sidebar = !visibilities.sidebar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ Scope {
|
||||
name: "show-osd"
|
||||
|
||||
onPressed: {
|
||||
const visibilities = Visibilities.getForActive()
|
||||
visibilities.osd = !visibilities.osd
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.osd = !visibilities.osd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ Scope {
|
||||
name: "toggle-settings"
|
||||
|
||||
onPressed: {
|
||||
const visibilities = Visibilities.getForActive()
|
||||
visibilities.settings = !visibilities.settings
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.settings = !visibilities.settings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-13
@@ -5,25 +5,26 @@ import QtQuick
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property alias enabled: clock.enabled
|
||||
readonly property date date: clock.date
|
||||
readonly property int hours: clock.hours
|
||||
readonly property int minutes: clock.minutes
|
||||
readonly property int seconds: clock.seconds
|
||||
|
||||
readonly property string timeStr: format("hh:mm:ss")
|
||||
readonly property date date: clock.date
|
||||
readonly property string dateStr: format("ddd d MMM - hh:mm:ss")
|
||||
readonly property list<string> timeComponents: timeStr.split(":")
|
||||
readonly property string hourStr: timeComponents[0] ?? ""
|
||||
readonly property string minuteStr: timeComponents[1] ?? ""
|
||||
readonly property string secondStr: timeComponents[2] ?? ""
|
||||
property alias enabled: clock.enabled
|
||||
readonly property string hourStr: timeComponents[0] ?? ""
|
||||
readonly property int hours: clock.hours
|
||||
readonly property string minuteStr: timeComponents[1] ?? ""
|
||||
readonly property int minutes: clock.minutes
|
||||
readonly property string secondStr: timeComponents[2] ?? ""
|
||||
readonly property int seconds: clock.seconds
|
||||
readonly property list<string> timeComponents: timeStr.split(":")
|
||||
readonly property string timeStr: format("hh:mm:ss")
|
||||
|
||||
function format(fmt: string): string {
|
||||
return Qt.formatDateTime(clock.date, fmt);
|
||||
}
|
||||
function format(fmt: string): string {
|
||||
return Qt.formatDateTime(clock.date, fmt);
|
||||
}
|
||||
|
||||
SystemClock {
|
||||
id: clock
|
||||
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
}
|
||||
|
||||
+31
-30
@@ -7,28 +7,29 @@ import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property SystemTrayItem item
|
||||
required property PanelWindow bar
|
||||
required property PanelWindow bar
|
||||
property bool hasLoaded: false
|
||||
required property int ind
|
||||
required property Wrapper popouts
|
||||
required property SystemTrayItem item
|
||||
required property RowLayout loader
|
||||
property bool hasLoaded: false
|
||||
required property Wrapper popouts
|
||||
|
||||
StateLayer {
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
radius: 6
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
|
||||
onClicked: {
|
||||
if ( mouse.button === Qt.LeftButton ) {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
root.item.activate();
|
||||
} else if ( mouse.button === Qt.RightButton ) {
|
||||
root.popouts.currentName = `traymenu${ root.ind }`;
|
||||
root.popouts.currentCenter = Qt.binding( () => root.mapToItem( root.loader, root.implicitWidth / 2, 0 ).x );
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
root.popouts.currentName = `traymenu${root.ind}`;
|
||||
root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x);
|
||||
root.popouts.hasCurrent = true;
|
||||
if ( visibilities.sidebar || visibilities.dashboard ) {
|
||||
if (visibilities.sidebar || visibilities.dashboard) {
|
||||
visibilities.sidebar = false;
|
||||
visibilities.dashboard = false;
|
||||
}
|
||||
@@ -40,27 +41,27 @@ Item {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
source: root.item.icon
|
||||
implicitSize: 22
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
implicitSize: 22
|
||||
layer.enabled: DynamicColors.light
|
||||
source: root.item.icon
|
||||
}
|
||||
|
||||
// Image {
|
||||
// id: icon
|
||||
//
|
||||
// property bool batteryHDPI: root.bar.screen.x < 0 && root.item.icon.includes("battery")
|
||||
// property bool nmHDPI: root.bar.screen.x < 0 && root.item.icon.includes("nm-")
|
||||
//
|
||||
// anchors.centerIn: parent
|
||||
// width: batteryHDPI ? 26 : ( nmHDPI ? 25 : 22 )
|
||||
// height: batteryHDPI ? 26 : ( nmHDPI ? 25 : 22 )
|
||||
// source: root.item.icon
|
||||
// mipmap: true
|
||||
// smooth: ( batteryHDPI || nmHDPI ) ? false : true
|
||||
// asynchronous: true
|
||||
// sourceSize.width: ( batteryHDPI || nmHDPI ) ? 16 : 22
|
||||
// sourceSize.height: ( batteryHDPI || nmHDPI ) ? 16 : 22
|
||||
// fillMode: Image.PreserveAspectFit
|
||||
// }
|
||||
// Image {
|
||||
// id: icon
|
||||
//
|
||||
// property bool batteryHDPI: root.bar.screen.x < 0 && root.item.icon.includes("battery")
|
||||
// property bool nmHDPI: root.bar.screen.x < 0 && root.item.icon.includes("nm-")
|
||||
//
|
||||
// anchors.centerIn: parent
|
||||
// width: batteryHDPI ? 26 : ( nmHDPI ? 25 : 22 )
|
||||
// height: batteryHDPI ? 26 : ( nmHDPI ? 25 : 22 )
|
||||
// source: root.item.icon
|
||||
// mipmap: true
|
||||
// smooth: ( batteryHDPI || nmHDPI ) ? false : true
|
||||
// asynchronous: true
|
||||
// sourceSize.width: ( batteryHDPI || nmHDPI ) ? 16 : 22
|
||||
// sourceSize.height: ( batteryHDPI || nmHDPI ) ? 16 : 22
|
||||
// fillMode: Image.PreserveAspectFit
|
||||
// }
|
||||
}
|
||||
|
||||
+347
-311
@@ -10,347 +10,383 @@ import qs.Effects
|
||||
import qs.Config
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
signal menuActionTriggered()
|
||||
signal finishedLoading()
|
||||
required property QsMenuHandle trayMenu
|
||||
required property point trayItemRect
|
||||
required property PanelWindow bar
|
||||
property var menuStack: []
|
||||
property real scaleValue: 0
|
||||
property alias focusGrab: grab.active
|
||||
property int entryHeight: 30
|
||||
property int biggestWidth: 0
|
||||
property int menuItemCount: menuOpener.children.values.length
|
||||
property color backgroundColor: DynamicColors.tPalette.m3surface
|
||||
required property PanelWindow bar
|
||||
property int biggestWidth: 0
|
||||
property color disabledHighlightColor: DynamicColors.layer(DynamicColors.palette.m3primaryContainer, 0)
|
||||
property color disabledTextColor: DynamicColors.layer(DynamicColors.palette.m3onSurface, 0)
|
||||
property int entryHeight: 30
|
||||
property alias focusGrab: grab.active
|
||||
property color highlightColor: DynamicColors.tPalette.m3primaryContainer
|
||||
property int menuItemCount: menuOpener.children.values.length
|
||||
property var menuStack: []
|
||||
property real scaleValue: 0
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
required property point trayItemRect
|
||||
required property QsMenuHandle trayMenu
|
||||
|
||||
property color backgroundColor: DynamicColors.tPalette.m3surface
|
||||
property color highlightColor: DynamicColors.tPalette.m3primaryContainer
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
property color disabledHighlightColor: DynamicColors.layer(DynamicColors.palette.m3primaryContainer, 0)
|
||||
property color disabledTextColor: DynamicColors.layer(DynamicColors.palette.m3onSurface, 0)
|
||||
signal finishedLoading
|
||||
signal menuActionTriggered
|
||||
|
||||
QsMenuOpener {
|
||||
id: menuOpener
|
||||
menu: root.trayMenu
|
||||
}
|
||||
function goBack() {
|
||||
if (root.menuStack.length > 0) {
|
||||
menuChangeAnimation.start();
|
||||
root.biggestWidth = 0;
|
||||
root.trayMenu = root.menuStack.pop();
|
||||
listLayout.positionViewAtBeginning();
|
||||
backEntry.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// onTrayMenuChanged: {
|
||||
// listLayout.forceLayout();
|
||||
// }
|
||||
function updateMask() {
|
||||
root.mask.changed();
|
||||
}
|
||||
|
||||
visible: false
|
||||
color: "transparent"
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
mask: Region { id: mask; item: menuRect }
|
||||
// onTrayMenuChanged: {
|
||||
// listLayout.forceLayout();
|
||||
// }
|
||||
|
||||
function goBack() {
|
||||
if ( root.menuStack.length > 0 ) {
|
||||
menuChangeAnimation.start();
|
||||
root.biggestWidth = 0;
|
||||
root.trayMenu = root.menuStack.pop();
|
||||
listLayout.positionViewAtBeginning();
|
||||
backEntry.visible = false;
|
||||
}
|
||||
}
|
||||
visible: false
|
||||
|
||||
function updateMask() {
|
||||
root.mask.changed();
|
||||
}
|
||||
mask: Region {
|
||||
id: mask
|
||||
|
||||
onVisibleChanged: {
|
||||
if ( !visible )
|
||||
root.menuStack.pop();
|
||||
backEntry.visible = false;
|
||||
item: menuRect
|
||||
}
|
||||
|
||||
openAnim.start();
|
||||
}
|
||||
onMenuActionTriggered: {
|
||||
if (root.menuStack.length > 0) {
|
||||
backEntry.visible = true;
|
||||
}
|
||||
}
|
||||
onVisibleChanged: {
|
||||
if (!visible)
|
||||
root.menuStack.pop();
|
||||
backEntry.visible = false;
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [ root ]
|
||||
active: false
|
||||
onCleared: {
|
||||
closeAnim.start();
|
||||
}
|
||||
}
|
||||
openAnim.start();
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: menuChangeAnimation
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 0
|
||||
property: "x"
|
||||
target: translateAnim
|
||||
to: -listLayout.width / 2
|
||||
}
|
||||
QsMenuOpener {
|
||||
id: menuOpener
|
||||
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.standard
|
||||
from: 1
|
||||
property: "opacity"
|
||||
target: columnLayout
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
menu: root.trayMenu
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
property: "menu"
|
||||
target: columnLayout
|
||||
}
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.standard
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: columnLayout
|
||||
to: 1
|
||||
}
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: listLayout.width / 2
|
||||
property: "x"
|
||||
target: translateAnim
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
active: false
|
||||
windows: [root]
|
||||
|
||||
onMenuActionTriggered: {
|
||||
if ( root.menuStack.length > 0 ) {
|
||||
backEntry.visible = true;
|
||||
}
|
||||
}
|
||||
onCleared: {
|
||||
closeAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: closeAnim
|
||||
Anim {
|
||||
target: menuRect
|
||||
property: "implicitHeight"
|
||||
to: 0
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
Anim {
|
||||
targets: [ menuRect, shadowRect ]
|
||||
property: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
onFinished: {
|
||||
root.visible = false;
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: menuChangeAnimation
|
||||
|
||||
ParallelAnimation {
|
||||
id: openAnim
|
||||
Anim {
|
||||
target: menuRect
|
||||
property: "implicitHeight"
|
||||
from: 0
|
||||
to: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
Anim {
|
||||
targets: [ menuRect, shadowRect ]
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 0
|
||||
property: "x"
|
||||
target: translateAnim
|
||||
to: -listLayout.width / 2
|
||||
}
|
||||
|
||||
ShadowRect {
|
||||
id: shadowRect
|
||||
anchors.fill: menuRect
|
||||
radius: menuRect.radius
|
||||
}
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.standard
|
||||
from: 1
|
||||
property: "opacity"
|
||||
target: columnLayout
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menuRect
|
||||
x: Math.round( root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11 )
|
||||
y: Math.round( root.trayItemRect.y - 5 )
|
||||
implicitWidth: listLayout.contentWidth + 10
|
||||
implicitHeight: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
|
||||
color: root.backgroundColor
|
||||
radius: 8
|
||||
clip: true
|
||||
PropertyAction {
|
||||
property: "menu"
|
||||
target: columnLayout
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.standard
|
||||
from: 0
|
||||
property: "opacity"
|
||||
target: columnLayout
|
||||
to: 1
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.standardTime / 2
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: listLayout.width / 2
|
||||
property: "x"
|
||||
target: translateAnim
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
spacing: 0
|
||||
transform: [
|
||||
Translate {
|
||||
id: translateAnim
|
||||
x: 0
|
||||
y: 0
|
||||
}
|
||||
]
|
||||
ListView {
|
||||
id: listLayout
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
spacing: 0
|
||||
contentWidth: root.biggestWidth
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
model: menuOpener.children
|
||||
ParallelAnimation {
|
||||
id: closeAnim
|
||||
|
||||
delegate: Rectangle {
|
||||
id: menuItem
|
||||
required property int index
|
||||
required property QsMenuEntry modelData
|
||||
property var child: QsMenuOpener {
|
||||
menu: menuItem.modelData
|
||||
}
|
||||
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
||||
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
|
||||
width: widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
|
||||
color: menuItem.modelData.isSeparator ? "#20FFFFFF" : containsMouseAndEnabled ? root.highlightColor : containsMouseAndNotEnabled ? root.disabledHighlightColor : "transparent"
|
||||
radius: 4
|
||||
visible: true
|
||||
onFinished: {
|
||||
root.visible = false;
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
property: "implicitHeight"
|
||||
target: menuRect
|
||||
to: 0
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var biggestWidth = root.biggestWidth;
|
||||
var currentWidth = widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20;
|
||||
if ( currentWidth > biggestWidth ) {
|
||||
root.biggestWidth = currentWidth;
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 1
|
||||
property: "opacity"
|
||||
targets: [menuRect, shadowRect]
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: widthMetrics
|
||||
text: menuItem.modelData.text
|
||||
}
|
||||
ParallelAnimation {
|
||||
id: openAnim
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
propagateComposedEvents: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: {
|
||||
if ( !menuItem.modelData.hasChildren ) {
|
||||
if ( menuItem.modelData.enabled ) {
|
||||
menuItem.modelData.triggered();
|
||||
closeAnim.start();
|
||||
}
|
||||
} else {
|
||||
root.menuStack.push(root.trayMenu);
|
||||
menuChangeAnimation.start();
|
||||
root.biggestWidth = 0;
|
||||
root.trayMenu = menuItem.modelData;
|
||||
listLayout.positionViewAtBeginning();
|
||||
root.menuActionTriggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 0
|
||||
property: "implicitHeight"
|
||||
target: menuRect
|
||||
to: listLayout.contentHeight + (root.menuStack.length > 0 ? root.entryHeight + 10 : 10)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Text {
|
||||
id: menuText
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.leftMargin: 10
|
||||
text: menuItem.modelData.text
|
||||
color: menuItem.modelData.enabled ? root.textColor : root.disabledTextColor
|
||||
}
|
||||
Image {
|
||||
id: iconImage
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.rightMargin: 10
|
||||
Layout.maximumWidth: 20
|
||||
Layout.maximumHeight: 20
|
||||
source: menuItem.modelData.icon
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
layer.enabled: true
|
||||
layer.effect: ColorOverlay {
|
||||
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||
}
|
||||
}
|
||||
Text {
|
||||
id: textArrow
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.rightMargin: 10
|
||||
Layout.bottomMargin: 5
|
||||
Layout.maximumWidth: 20
|
||||
Layout.maximumHeight: 20
|
||||
text: ""
|
||||
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||
visible: menuItem.modelData.hasChildren ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: backEntry
|
||||
visible: false
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.entryHeight
|
||||
color: mouseAreaBack.containsMouse ? "#15FFFFFF" : "transparent"
|
||||
radius: 4
|
||||
Anim {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
from: 0
|
||||
property: "opacity"
|
||||
targets: [menuRect, shadowRect]
|
||||
to: 1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseAreaBack
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
root.goBack();
|
||||
}
|
||||
}
|
||||
ShadowRect {
|
||||
id: shadowRect
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: "Back "
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
anchors.fill: menuRect
|
||||
radius: menuRect.radius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: menuRect
|
||||
|
||||
clip: true
|
||||
color: root.backgroundColor
|
||||
implicitHeight: listLayout.contentHeight + (root.menuStack.length > 0 ? root.entryHeight + 10 : 10)
|
||||
implicitWidth: listLayout.contentWidth + 10
|
||||
radius: 8
|
||||
x: Math.round(root.trayItemRect.x - (menuRect.implicitWidth / 2) + 11)
|
||||
y: Math.round(root.trayItemRect.y - 5)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
spacing: 0
|
||||
|
||||
transform: [
|
||||
Translate {
|
||||
id: translateAnim
|
||||
|
||||
x: 0
|
||||
y: 0
|
||||
}
|
||||
]
|
||||
|
||||
ListView {
|
||||
id: listLayout
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
contentWidth: root.biggestWidth
|
||||
model: menuOpener.children
|
||||
spacing: 0
|
||||
|
||||
delegate: Rectangle {
|
||||
id: menuItem
|
||||
|
||||
property var child: QsMenuOpener {
|
||||
menu: menuItem.modelData
|
||||
}
|
||||
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
||||
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
|
||||
required property int index
|
||||
required property QsMenuEntry modelData
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
color: menuItem.modelData.isSeparator ? "#20FFFFFF" : containsMouseAndEnabled ? root.highlightColor : containsMouseAndNotEnabled ? root.disabledHighlightColor : "transparent"
|
||||
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
|
||||
radius: 4
|
||||
visible: true
|
||||
width: widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20
|
||||
|
||||
Behavior on color {
|
||||
CAnim {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
var biggestWidth = root.biggestWidth;
|
||||
var currentWidth = widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20;
|
||||
if (currentWidth > biggestWidth) {
|
||||
root.biggestWidth = currentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: widthMetrics
|
||||
|
||||
text: menuItem.modelData.text
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
acceptedButtons: Qt.LeftButton
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
propagateComposedEvents: true
|
||||
|
||||
onClicked: {
|
||||
if (!menuItem.modelData.hasChildren) {
|
||||
if (menuItem.modelData.enabled) {
|
||||
menuItem.modelData.triggered();
|
||||
closeAnim.start();
|
||||
}
|
||||
} else {
|
||||
root.menuStack.push(root.trayMenu);
|
||||
menuChangeAnimation.start();
|
||||
root.biggestWidth = 0;
|
||||
root.trayMenu = menuItem.modelData;
|
||||
listLayout.positionViewAtBeginning();
|
||||
root.menuActionTriggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Text {
|
||||
id: menuText
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
Layout.leftMargin: 10
|
||||
color: menuItem.modelData.enabled ? root.textColor : root.disabledTextColor
|
||||
text: menuItem.modelData.text
|
||||
}
|
||||
|
||||
Image {
|
||||
id: iconImage
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.maximumHeight: 20
|
||||
Layout.maximumWidth: 20
|
||||
Layout.rightMargin: 10
|
||||
fillMode: Image.PreserveAspectFit
|
||||
layer.enabled: true
|
||||
source: menuItem.modelData.icon
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
|
||||
layer.effect: ColorOverlay {
|
||||
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: textArrow
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
Layout.bottomMargin: 5
|
||||
Layout.maximumHeight: 20
|
||||
Layout.maximumWidth: 20
|
||||
Layout.rightMargin: 10
|
||||
color: menuItem.modelData.enabled ? "white" : "gray"
|
||||
text: ""
|
||||
visible: menuItem.modelData.hasChildren ?? false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: backEntry
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.entryHeight
|
||||
color: mouseAreaBack.containsMouse ? "#15FFFFFF" : "transparent"
|
||||
radius: 4
|
||||
visible: false
|
||||
|
||||
MouseArea {
|
||||
id: mouseAreaBack
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
root.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
color: "white"
|
||||
text: "Back "
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+189
-190
@@ -9,239 +9,238 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
|
||||
StackView {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
required property Item popouts
|
||||
required property QsMenuHandle trayItem
|
||||
property int biggestWidth: 0
|
||||
required property Item popouts
|
||||
property int rootWidth: 0
|
||||
required property QsMenuHandle trayItem
|
||||
|
||||
property int rootWidth: 0
|
||||
property int biggestWidth: 0
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
initialItem: SubMenu {
|
||||
handle: root.trayItem
|
||||
}
|
||||
popEnter: NoAnim {
|
||||
}
|
||||
popExit: NoAnim {
|
||||
}
|
||||
pushEnter: NoAnim {
|
||||
}
|
||||
pushExit: NoAnim {
|
||||
}
|
||||
|
||||
initialItem: SubMenu {
|
||||
handle: root.trayItem
|
||||
}
|
||||
Component {
|
||||
id: subMenuComp
|
||||
|
||||
pushEnter: NoAnim {}
|
||||
pushExit: NoAnim {}
|
||||
popEnter: NoAnim {}
|
||||
popExit: NoAnim {}
|
||||
SubMenu {
|
||||
}
|
||||
}
|
||||
|
||||
component NoAnim: Transition {
|
||||
NumberAnimation {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
component NoAnim: Transition {
|
||||
NumberAnimation {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
component SubMenu: Column {
|
||||
id: menu
|
||||
|
||||
component SubMenu: Column {
|
||||
id: menu
|
||||
required property QsMenuHandle handle
|
||||
property bool isSubMenu
|
||||
property bool shown
|
||||
|
||||
required property QsMenuHandle handle
|
||||
property bool isSubMenu
|
||||
property bool shown
|
||||
opacity: shown ? 1 : 0
|
||||
padding: 0
|
||||
scale: shown ? 1 : 0.8
|
||||
spacing: 4
|
||||
|
||||
padding: 0
|
||||
spacing: 4
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
|
||||
opacity: shown ? 1 : 0
|
||||
scale: shown ? 1 : 0.8
|
||||
Component.onCompleted: shown = true
|
||||
StackView.onActivating: shown = true
|
||||
StackView.onDeactivating: shown = false
|
||||
StackView.onRemoved: destroy()
|
||||
|
||||
Component.onCompleted: shown = true
|
||||
StackView.onActivating: shown = true
|
||||
StackView.onDeactivating: shown = false
|
||||
StackView.onRemoved: destroy()
|
||||
QsMenuOpener {
|
||||
id: menuOpener
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
menu: menu.handle
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {}
|
||||
}
|
||||
Repeater {
|
||||
model: menuOpener.children
|
||||
|
||||
QsMenuOpener {
|
||||
id: menuOpener
|
||||
CustomRect {
|
||||
id: item
|
||||
|
||||
menu: menu.handle
|
||||
}
|
||||
required property QsMenuEntry modelData
|
||||
|
||||
Repeater {
|
||||
model: menuOpener.children
|
||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
||||
implicitWidth: root.biggestWidth
|
||||
radius: 4
|
||||
|
||||
CustomRect {
|
||||
id: item
|
||||
Loader {
|
||||
id: children
|
||||
|
||||
required property QsMenuEntry modelData
|
||||
active: !item.modelData.isSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
asynchronous: true
|
||||
|
||||
implicitWidth: root.biggestWidth
|
||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
||||
sourceComponent: Item {
|
||||
implicitHeight: 30
|
||||
|
||||
radius: 4
|
||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
const entry = item.modelData;
|
||||
if (entry.hasChildren) {
|
||||
root.rootWidth = root.biggestWidth;
|
||||
root.biggestWidth = 0;
|
||||
root.push(subMenuComp.createObject(null, {
|
||||
handle: entry,
|
||||
isSubMenu: true
|
||||
}));
|
||||
} else {
|
||||
item.modelData.triggered();
|
||||
root.popouts.hasCurrent = false;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: children
|
||||
disabled: !item.modelData.enabled
|
||||
radius: item.radius
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
Loader {
|
||||
id: icon
|
||||
|
||||
active: !item.modelData.isSeparator
|
||||
asynchronous: true
|
||||
active: item.modelData.icon !== ""
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: 30
|
||||
sourceComponent: Item {
|
||||
implicitHeight: label.implicitHeight
|
||||
implicitWidth: label.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
radius: item.radius
|
||||
disabled: !item.modelData.enabled
|
||||
IconImage {
|
||||
id: iconImage
|
||||
|
||||
function onClicked(): void {
|
||||
const entry = item.modelData;
|
||||
if (entry.hasChildren) {
|
||||
root.rootWidth = root.biggestWidth;
|
||||
root.biggestWidth = 0;
|
||||
root.push(subMenuComp.createObject(null, {
|
||||
handle: entry,
|
||||
isSubMenu: true
|
||||
}));
|
||||
} else {
|
||||
item.modelData.triggered();
|
||||
root.popouts.hasCurrent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
implicitSize: parent.implicitHeight
|
||||
source: item.modelData.icon
|
||||
visible: false
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: icon
|
||||
MultiEffect {
|
||||
anchors.fill: iconImage
|
||||
colorization: 1.0
|
||||
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
source: iconImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: 10
|
||||
CustomText {
|
||||
id: label
|
||||
|
||||
active: item.modelData.icon !== ""
|
||||
asynchronous: true
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
text: labelMetrics.elidedText
|
||||
}
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: label.implicitHeight
|
||||
implicitWidth: label.implicitHeight
|
||||
IconImage {
|
||||
id: iconImage
|
||||
implicitSize: parent.implicitHeight
|
||||
source: item.modelData.icon
|
||||
visible: false
|
||||
}
|
||||
TextMetrics {
|
||||
id: labelMetrics
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: iconImage
|
||||
source: iconImage
|
||||
colorization: 1.0
|
||||
colorizationColor: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
}
|
||||
}
|
||||
}
|
||||
font.family: label.font.family
|
||||
font.pointSize: label.font.pointSize
|
||||
text: item.modelData.text
|
||||
|
||||
CustomText {
|
||||
id: label
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 10
|
||||
Loader {
|
||||
id: expand
|
||||
|
||||
text: labelMetrics.elidedText
|
||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
}
|
||||
active: item.modelData.hasChildren
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
asynchronous: true
|
||||
|
||||
TextMetrics {
|
||||
id: labelMetrics
|
||||
sourceComponent: MaterialIcon {
|
||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
text: "chevron_right"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text: item.modelData.text
|
||||
font.pointSize: label.font.pointSize
|
||||
font.family: label.font.family
|
||||
Loader {
|
||||
active: menu.isSubMenu
|
||||
asynchronous: true
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceComponent: Item {
|
||||
implicitHeight: back.implicitHeight + 2 / 2
|
||||
implicitWidth: back.implicitWidth
|
||||
|
||||
Loader {
|
||||
id: expand
|
||||
Item {
|
||||
anchors.bottom: parent.bottom
|
||||
implicitHeight: back.implicitHeight
|
||||
implicitWidth: back.implicitWidth + 10
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
color: DynamicColors.palette.m3secondaryContainer
|
||||
radius: 4
|
||||
|
||||
active: item.modelData.hasChildren
|
||||
asynchronous: true
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
root.pop();
|
||||
root.biggestWidth = root.rootWidth;
|
||||
}
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "chevron_right"
|
||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
radius: parent.radius
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: menu.isSubMenu
|
||||
asynchronous: true
|
||||
Row {
|
||||
id: back
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitWidth: back.implicitWidth
|
||||
implicitHeight: back.implicitHeight + 2 / 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Item {
|
||||
anchors.bottom: parent.bottom
|
||||
implicitWidth: back.implicitWidth + 10
|
||||
implicitHeight: back.implicitHeight
|
||||
MaterialIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
text: "chevron_left"
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
anchors.fill: parent
|
||||
radius: 4
|
||||
color: DynamicColors.palette.m3secondaryContainer
|
||||
|
||||
StateLayer {
|
||||
radius: parent.radius
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
|
||||
function onClicked(): void {
|
||||
root.pop();
|
||||
root.biggestWidth = root.rootWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: back
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MaterialIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "chevron_left"
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: qsTr("Back")
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: subMenuComp
|
||||
|
||||
SubMenu {}
|
||||
}
|
||||
CustomText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onSecondaryContainer
|
||||
text: qsTr("Back")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-21
@@ -6,32 +6,35 @@ import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
|
||||
Row {
|
||||
id: root
|
||||
id: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
required property PanelWindow bar
|
||||
required property Wrapper popouts
|
||||
required property PanelWindow bar
|
||||
readonly property alias items: repeater
|
||||
required property RowLayout loader
|
||||
readonly property alias items: repeater
|
||||
required property Wrapper popouts
|
||||
|
||||
spacing: 0
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
|
||||
model: SystemTray.items
|
||||
|
||||
TrayItem {
|
||||
id: trayItem
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: SystemTray.items
|
||||
TrayItem {
|
||||
id: trayItem
|
||||
required property SystemTrayItem modelData
|
||||
required property int index
|
||||
required property SystemTrayItem modelData
|
||||
|
||||
bar: root.bar
|
||||
implicitHeight: 34
|
||||
implicitWidth: 34
|
||||
ind: index
|
||||
popouts: root.popouts
|
||||
item: modelData
|
||||
loader: root.loader
|
||||
implicitHeight: 34
|
||||
implicitWidth: 34
|
||||
item: modelData
|
||||
bar: root.bar
|
||||
}
|
||||
}
|
||||
popouts: root.popouts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+110
-114
@@ -11,173 +11,169 @@ Item {
|
||||
|
||||
required property var wrapper
|
||||
|
||||
implicitWidth: profiles.implicitWidth
|
||||
implicitHeight: profiles.implicitHeight
|
||||
implicitWidth: profiles.implicitWidth
|
||||
|
||||
CustomRect {
|
||||
id: profiles
|
||||
CustomRect {
|
||||
id: profiles
|
||||
|
||||
property string current: {
|
||||
const p = PowerProfiles.profile;
|
||||
if (p === PowerProfile.PowerSaver)
|
||||
return saver.icon;
|
||||
if (p === PowerProfile.Performance)
|
||||
return perf.icon;
|
||||
return balance.icon;
|
||||
}
|
||||
property string current: {
|
||||
const p = PowerProfiles.profile;
|
||||
if (p === PowerProfile.PowerSaver)
|
||||
return saver.icon;
|
||||
if (p === PowerProfile.Performance)
|
||||
return perf.icon;
|
||||
return balance.icon;
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
||||
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
||||
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
||||
// color: "transparent"
|
||||
radius: 6
|
||||
radius: 6
|
||||
|
||||
CustomRect {
|
||||
id: indicator
|
||||
CustomRect {
|
||||
id: indicator
|
||||
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
state: profiles.current
|
||||
color: DynamicColors.palette.m3primary
|
||||
radius: 1000
|
||||
state: profiles.current
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: saver.icon
|
||||
states: [
|
||||
State {
|
||||
name: saver.icon
|
||||
|
||||
Fill {
|
||||
item: saver
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: balance.icon
|
||||
Fill {
|
||||
item: saver
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: balance.icon
|
||||
|
||||
Fill {
|
||||
item: balance
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: perf.icon
|
||||
Fill {
|
||||
item: balance
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: perf.icon
|
||||
|
||||
Fill {
|
||||
item: perf
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
Fill {
|
||||
item: perf
|
||||
}
|
||||
}
|
||||
]
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Profile {
|
||||
id: saver
|
||||
Profile {
|
||||
id: saver
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 25
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 25
|
||||
|
||||
icon: "nest_eco_leaf"
|
||||
profile: PowerProfile.PowerSaver
|
||||
text: "Power Saver"
|
||||
profile: PowerProfile.PowerSaver
|
||||
icon: "nest_eco_leaf"
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: saverLabel
|
||||
anchors.top: saver.bottom
|
||||
|
||||
anchors.horizontalCenter: saver.horizontalCenter
|
||||
anchors.top: saver.bottom
|
||||
font.bold: true
|
||||
text: saver.text
|
||||
}
|
||||
|
||||
Profile {
|
||||
id: balance
|
||||
Profile {
|
||||
id: balance
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
icon: "power_settings_new"
|
||||
profile: PowerProfile.Balanced
|
||||
text: "Balanced"
|
||||
profile: PowerProfile.Balanced
|
||||
icon: "power_settings_new"
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: balanceLabel
|
||||
anchors.top: balance.bottom
|
||||
|
||||
anchors.horizontalCenter: balance.horizontalCenter
|
||||
anchors.top: balance.bottom
|
||||
font.bold: true
|
||||
text: balance.text
|
||||
}
|
||||
|
||||
Profile {
|
||||
id: perf
|
||||
Profile {
|
||||
id: perf
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 25
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 25
|
||||
|
||||
icon: "bolt"
|
||||
profile: PowerProfile.Performance
|
||||
text: "Performance"
|
||||
profile: PowerProfile.Performance
|
||||
icon: "bolt"
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: perfLabel
|
||||
anchors.top: perf.bottom
|
||||
|
||||
anchors.horizontalCenter: perf.horizontalCenter
|
||||
anchors.top: perf.bottom
|
||||
font.bold: true
|
||||
text: perf.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Fill: AnchorChanges {
|
||||
required property Item item
|
||||
|
||||
target: indicator
|
||||
anchors.left: item.left
|
||||
anchors.right: item.right
|
||||
anchors.top: item.top
|
||||
anchors.bottom: item.bottom
|
||||
}
|
||||
component Fill: AnchorChanges {
|
||||
required property Item item
|
||||
|
||||
anchors.bottom: item.bottom
|
||||
anchors.left: item.left
|
||||
anchors.right: item.right
|
||||
anchors.top: item.top
|
||||
target: indicator
|
||||
}
|
||||
component Profile: Item {
|
||||
required property string icon
|
||||
required property int profile
|
||||
required property string icon
|
||||
required property int profile
|
||||
required property string text
|
||||
|
||||
implicitWidth: icon.implicitHeight + 5 * 2
|
||||
implicitHeight: icon.implicitHeight + 5 * 2
|
||||
implicitHeight: icon.implicitHeight + 5 * 2
|
||||
implicitWidth: icon.implicitHeight + 5 * 2
|
||||
|
||||
StateLayer {
|
||||
radius: 1000
|
||||
color: profiles.current === parent.icon ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
StateLayer {
|
||||
function onClicked(): void {
|
||||
PowerProfiles.profile = parent.profile;
|
||||
}
|
||||
|
||||
function onClicked(): void {
|
||||
PowerProfiles.profile = parent.profile;
|
||||
}
|
||||
}
|
||||
color: profiles.current === parent.icon ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
radius: 1000
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.centerIn: parent
|
||||
color: profiles.current === text ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
fill: profiles.current === text ? 1 : 0
|
||||
font.pointSize: 36
|
||||
text: parent.icon
|
||||
|
||||
text: parent.icon
|
||||
font.pointSize: 36
|
||||
color: profiles.current === text ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
fill: profiles.current === text ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,25 +8,28 @@ import qs.Helpers as Helpers
|
||||
Item {
|
||||
id: root
|
||||
|
||||
implicitWidth: layout.childrenRect.width + 10 * 2
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
implicitWidth: layout.childrenRect.width + 10 * 2
|
||||
|
||||
CustomRect {
|
||||
anchors.bottomMargin: 4
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 4
|
||||
anchors.bottomMargin: 4
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: 1000
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3error
|
||||
fill: 1
|
||||
text: {
|
||||
if (!Helpers.UPower.displayDevice.isLaptopBattery) {
|
||||
if (PowerProfiles.profile === PowerProfile.PowerSaver)
|
||||
@@ -45,8 +48,6 @@ Item {
|
||||
level--;
|
||||
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
|
||||
}
|
||||
color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3error
|
||||
fill: 1
|
||||
}
|
||||
|
||||
CustomText {
|
||||
|
||||
+24
-22
@@ -7,29 +7,31 @@ import Quickshell.Io
|
||||
import qs.Modules
|
||||
|
||||
Singleton {
|
||||
property int availableUpdates: 0
|
||||
property int availableUpdates: 0
|
||||
|
||||
Timer {
|
||||
interval: 1
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
updatesProc.running = true
|
||||
interval = 5000
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
interval: 1
|
||||
repeat: true
|
||||
running: true
|
||||
|
||||
Process {
|
||||
id: updatesProc
|
||||
running: false
|
||||
onTriggered: {
|
||||
updatesProc.running = true;
|
||||
interval = 5000;
|
||||
}
|
||||
}
|
||||
|
||||
command: ["checkupdates"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const output = this.text
|
||||
const lines = output.trim().split("\n").filter(line => line.length > 0)
|
||||
availableUpdates = lines.length
|
||||
}
|
||||
}
|
||||
}
|
||||
Process {
|
||||
id: updatesProc
|
||||
|
||||
command: ["checkupdates"]
|
||||
running: false
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const output = this.text;
|
||||
const lines = output.trim().split("\n").filter(line => line.length > 0);
|
||||
availableUpdates = lines.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user