1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Add an ArchUpdater widget (#201)

This commit is contained in:
Aziz Hasanain
2025-09-15 15:54:39 +03:00
committed by GitHub
parent 8ee43de145
commit e4f86abda9
6 changed files with 615 additions and 1 deletions

View File

@@ -0,0 +1,353 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
DankPopout {
id: archUpdaterPopout
property var parentWidget: null
property string triggerSection: "right"
property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) {
triggerX = x;
triggerY = y;
triggerWidth = width;
triggerSection = section;
triggerScreen = screen;
}
popupWidth: 400
popupHeight: 500
triggerX: Screen.width - 600 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
triggerWidth: 55
positioning: "center"
screen: triggerScreen
visible: shouldBeVisible
shouldBeVisible: false
onShouldBeVisibleChanged: {
if (shouldBeVisible) {
if (ArchUpdaterService.updateCount === 0 && !ArchUpdaterService.isChecking) {
ArchUpdaterService.checkForUpdates()
}
}
}
content: Component {
Rectangle {
id: updaterPanel
color: Theme.popupBackground()
radius: Theme.cornerRadius
antialiasing: true
smooth: true
Repeater {
model: [{
"margin": -3,
"color": Qt.rgba(0, 0, 0, 0.05),
"z": -3
}, {
"margin": -2,
"color": Qt.rgba(0, 0, 0, 0.08),
"z": -2
}, {
"margin": 0,
"color": Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12),
"z": -1
}]
Rectangle {
anchors.fill: parent
anchors.margins: modelData.margin
color: "transparent"
radius: parent.radius + Math.abs(modelData.margin)
border.color: modelData.color
border.width: 1
z: modelData.z
}
}
Column {
width: parent.width - Theme.spacingL * 2
height: parent.height - Theme.spacingL * 2
x: Theme.spacingL
y: Theme.spacingL
spacing: Theme.spacingL
Item {
width: parent.width
height: 40
StyledText {
text: "System Updates"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: {
if (ArchUpdaterService.isChecking) return "Checking...";
if (ArchUpdaterService.hasError) return "Error";
if (ArchUpdaterService.updateCount === 0) return "Up to date";
return ArchUpdaterService.updateCount + " updates";
}
font.pixelSize: Theme.fontSizeMedium
color: {
if (ArchUpdaterService.hasError) return Theme.error;
return Theme.surfaceText;
}
}
DankActionButton {
id: checkForUpdatesButton
buttonSize: 28
iconName: "refresh"
iconSize: 18
z: 15
iconColor: Theme.surfaceText
enabled: !ArchUpdaterService.isChecking
opacity: enabled ? 1.0 : 0.5
onClicked: {
ArchUpdaterService.checkForUpdates()
}
RotationAnimation {
target: checkForUpdatesButton
property: "rotation"
from: 0
to: 360
duration: 1000
running: ArchUpdaterService.isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
checkForUpdatesButton.rotation = 0
}
}
}
}
}
}
Rectangle {
width: parent.width
height: {
let usedHeight = 40 + Theme.spacingL
usedHeight += 48 + Theme.spacingL
return parent.height - usedHeight
}
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
border.width: 1
Column {
anchors.fill: parent
anchors.margins: Theme.spacingM
anchors.rightMargin: 0
StyledText {
id: statusText
width: parent.width
text: {
if (ArchUpdaterService.hasError) {
return "Failed to check for updates:\n" + ArchUpdaterService.errorMessage;
}
if (!ArchUpdaterService.helperAvailable) {
return "No AUR helper found. Please install 'paru' or 'yay' to check for updates.";
}
if (ArchUpdaterService.isChecking) {
return "Checking for updates...";
}
if (ArchUpdaterService.updateCount === 0) {
return "Your system is up to date!";
}
return `Found ${ArchUpdaterService.updateCount} packages to update:`;
}
font.pixelSize: Theme.fontSizeMedium
color: {
if (ArchUpdaterService.hasError) return Theme.errorText;
return Theme.surfaceText;
}
wrapMode: Text.WordWrap
visible: ArchUpdaterService.updateCount === 0 || ArchUpdaterService.hasError || ArchUpdaterService.isChecking
}
DankListView {
id: packagesList
width: parent.width
height: parent.height - (ArchUpdaterService.updateCount === 0 || ArchUpdaterService.hasError || ArchUpdaterService.isChecking ? statusText.height + Theme.spacingM : 0)
visible: ArchUpdaterService.updateCount > 0 && !ArchUpdaterService.isChecking && !ArchUpdaterService.hasError
clip: true
spacing: Theme.spacingXS
model: ArchUpdaterService.availableUpdates
delegate: Rectangle {
width: ListView.view.width - Theme.spacingM
height: 48
radius: Theme.cornerRadius
color: packageMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent"
border.color: Theme.outlineLight
border.width: 1
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingM
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - 24 - Theme.spacingM
spacing: 2
StyledText {
width: parent.width
text: modelData.name || ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
}
StyledText {
width: parent.width
text: `${modelData.currentVersion} ${modelData.newVersion}`
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
}
}
}
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea {
id: packageMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
}
}
}
}
}
Row {
width: parent.width
height: 48
spacing: Theme.spacingM
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: parent.height
radius: Theme.cornerRadius
color: updateMouseArea.containsMouse ? Theme.primaryHover : Theme.secondaryHover
opacity: ArchUpdaterService.updateCount > 0 ? 1.0 : 0.5
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: "system_update_alt"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Update All"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: updateMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: ArchUpdaterService.updateCount > 0
onClicked: {
ArchUpdaterService.runUpdates()
archUpdaterPopout.close()
}
}
}
Rectangle {
width: (parent.width - Theme.spacingM) / 2
height: parent.height
radius: Theme.cornerRadius
color: closeMouseArea.containsMouse ? Theme.errorPressed : Theme.secondaryHover
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: "close"
size: Theme.iconSize
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Close"
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: closeMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
archUpdaterPopout.close()
}
}
}
}
}
}
}
}

View File

@@ -163,6 +163,12 @@ Item {
"description": "Quick access to color picker",
"icon": "palette",
"enabled": true
}, {
"id": "archUpdater",
"text": "Arch Updater",
"description": "Check for updates in Arch-based systems",
"icon": "update",
"enabled": true
}]
property var defaultLeftWidgets: [{
"id": "launcherButton",

View File

@@ -0,0 +1,110 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isActive: false
property string section: "right"
property var popupTarget: null
property var parentScreen: null
property real widgetHeight: 30
property real barHeight: 48
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
readonly property bool hasUpdates: ArchUpdaterService.updateCount > 0
readonly property bool isChecking: ArchUpdaterService.isChecking
signal clicked()
width: updaterIcon.width + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = updaterArea.containsMouse || root.isActive ? Theme.primaryPressed : Theme.secondaryHover;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
Row {
id: updaterIcon
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: statusIcon
anchors.verticalCenter: parent.verticalCenter
name: {
if (isChecking) return "refresh";
if (ArchUpdaterService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (ArchUpdaterService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimation
target: statusIcon
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIcon.rotation = 0
}
}
}
}
StyledText {
id: countText
anchors.verticalCenter: parent.verticalCenter
text: ArchUpdaterService.updateCount.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
visible: hasUpdates && !isChecking
}
}
MouseArea {
id: updaterArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0);
const currentScreen = parentScreen || Screen;
const screenX = currentScreen.x || 0;
const relativeX = globalPos.x - screenX;
popupTarget.setTriggerPosition(relativeX, barHeight + Theme.spacingXS, width, section, currentScreen);
}
root.clicked();
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -183,6 +183,9 @@ PanelWindow {
}, {
"loader": clipboardHistoryModalPopup,
"prop": "visible"
}, {
"loader": archUpdaterLoader,
"prop": "shouldBeVisible"
}]
return notepadInstanceVisible || loaders.some(item => {
if (item.loader) {
@@ -373,7 +376,8 @@ PanelWindow {
"keyboard_layout_name": keyboardLayoutNameComponent,
"vpn": vpnComponent,
"notepadButton": notepadButtonComponent,
"colorPicker": colorPickerComponent
"colorPicker": colorPickerComponent,
"archUpdater": archUpdaterComponent
})
function getWidgetComponent(widgetId) {
@@ -1016,6 +1020,26 @@ PanelWindow {
}
}
}
Component {
id: archUpdaterComponent
ArchUpdater {
isActive: archUpdaterLoader.item ? archUpdaterLoader.item.shouldBeVisible : false
widgetHeight: root.widgetHeight
barHeight: root.effectiveBarHeight
section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: {
archUpdaterLoader.active = true
return archUpdaterLoader.item
}
parentScreen: root.screen
onClicked: {
archUpdaterLoader.active = true
archUpdaterLoader.item?.toggle()
}
}
}
}
}
}