updates popout

This commit is contained in:
Zacharias-Brohn
2026-03-17 18:45:08 +01:00
parent 46a1af82f5
commit 152b363da2
10 changed files with 234 additions and 442 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ ShapePath {
readonly property bool flatten: wrapper.height < rounding * 2
property real ibr: invertBottomRounding ? -1 : 1
required property bool invertBottomRounding
readonly property real rounding: 8
property real rounding: Appearance.rounding.smallest
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
required property Wrapper wrapper
+16 -11
View File
@@ -4,16 +4,17 @@ import Quickshell
import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules as Bar
import qs.Modules
import qs.Config
import qs.Helpers
import qs.Modules.UPower
import qs.Modules.Network
import qs.Modules.Updates
RowLayout {
id: root
required property Bar.Wrapper popouts
required property Wrapper popouts
required property ShellScreen screen
readonly property int vPadding: 6
required property PersistentProperties visibilities
@@ -47,6 +48,10 @@ RowLayout {
popouts.currentName = "upower";
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
} else if (id === "updates") {
popouts.currentName = "updates";
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
popouts.hasCurrent = true;
}
}
@@ -73,7 +78,7 @@ RowLayout {
roleValue: "workspaces"
delegate: WrappedLoader {
sourceComponent: Bar.Workspaces {
sourceComponent: Workspaces {
screen: root.screen
}
}
@@ -83,7 +88,7 @@ RowLayout {
roleValue: "audio"
delegate: WrappedLoader {
sourceComponent: Bar.AudioWidget {
sourceComponent: AudioWidget {
}
}
}
@@ -92,7 +97,7 @@ RowLayout {
roleValue: "tray"
delegate: WrappedLoader {
sourceComponent: Bar.TrayWidget {
sourceComponent: TrayWidget {
loader: root
popouts: root.popouts
}
@@ -103,7 +108,7 @@ RowLayout {
roleValue: "resources"
delegate: WrappedLoader {
sourceComponent: Bar.Resources {
sourceComponent: Resources {
visibilities: root.visibilities
}
}
@@ -113,7 +118,7 @@ RowLayout {
roleValue: "updates"
delegate: WrappedLoader {
sourceComponent: Bar.UpdatesWidget {
sourceComponent: UpdatesWidget {
}
}
}
@@ -122,7 +127,7 @@ RowLayout {
roleValue: "notifBell"
delegate: WrappedLoader {
sourceComponent: Bar.NotifBell {
sourceComponent: NotifBell {
popouts: root.popouts
visibilities: root.visibilities
}
@@ -133,7 +138,7 @@ RowLayout {
roleValue: "clock"
delegate: WrappedLoader {
sourceComponent: Bar.Clock {
sourceComponent: Clock {
loader: root
popouts: root.popouts
visibilities: root.visibilities
@@ -145,7 +150,7 @@ RowLayout {
roleValue: "activeWindow"
delegate: WrappedLoader {
sourceComponent: Bar.WindowTitle {
sourceComponent: WindowTitle {
bar: root
}
}
@@ -173,7 +178,7 @@ RowLayout {
roleValue: "media"
delegate: WrappedLoader {
sourceComponent: Bar.MediaWidget {
sourceComponent: MediaWidget {
}
}
}
+9
View File
@@ -8,6 +8,7 @@ import qs.Components
import qs.Modules.WSOverview
import qs.Modules.Network
import qs.Modules.UPower
import qs.Modules.Updates
Item {
id: root
@@ -92,6 +93,14 @@ Item {
wrapper: root.wrapper
}
}
Popout {
name: "updates"
sourceComponent: UpdatesPopout {
wrapper: root.wrapper
}
}
}
component Popout: Loader {
-392
View File
@@ -1,392 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell.Hyprland
import QtQml
import qs.Effects
import qs.Config
PanelWindow {
id: root
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
signal finishedLoading
signal menuActionTriggered
function goBack() {
if (root.menuStack.length > 0) {
menuChangeAnimation.start();
root.biggestWidth = 0;
root.trayMenu = root.menuStack.pop();
listLayout.positionViewAtBeginning();
backEntry.visible = false;
}
}
function updateMask() {
root.mask.changed();
}
color: "transparent"
// onTrayMenuChanged: {
// listLayout.forceLayout();
// }
visible: false
mask: Region {
id: mask
item: menuRect
}
onMenuActionTriggered: {
if (root.menuStack.length > 0) {
backEntry.visible = true;
}
}
onVisibleChanged: {
if (!visible)
root.menuStack.pop();
backEntry.visible = false;
openAnim.start();
}
QsMenuOpener {
id: menuOpener
menu: root.trayMenu
}
anchors {
bottom: true
left: true
right: true
top: true
}
HyprlandFocusGrab {
id: grab
active: false
windows: [root]
onCleared: {
closeAnim.start();
}
}
SequentialAnimation {
id: menuChangeAnimation
ParallelAnimation {
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.expressiveEffects
from: 0
property: "x"
target: translateAnim
to: -listLayout.width / 2
}
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.standard
from: 1
property: "opacity"
target: columnLayout
to: 0
}
}
PropertyAction {
property: "menu"
target: columnLayout
}
ParallelAnimation {
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.standard
from: 0
property: "opacity"
target: columnLayout
to: 1
}
NumberAnimation {
duration: MaterialEasing.standardTime / 2
easing.bezierCurve: MaterialEasing.expressiveEffects
from: listLayout.width / 2
property: "x"
target: translateAnim
to: 0
}
}
}
ParallelAnimation {
id: closeAnim
onFinished: {
root.visible = false;
}
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: menuRect
to: 0
}
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
from: 1
property: "opacity"
targets: [menuRect, shadowRect]
to: 0
}
}
ParallelAnimation {
id: openAnim
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)
}
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
from: 0
property: "opacity"
targets: [menuRect, shadowRect]
to: 1
}
}
ShadowRect {
id: shadowRect
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
}
}
}
}
}
-37
View File
@@ -1,37 +0,0 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Modules
Singleton {
property int availableUpdates: 0
Timer {
interval: 1
repeat: true
running: true
onTriggered: {
updatesProc.running = true;
interval = 5000;
}
}
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;
}
}
}
}
+130
View File
@@ -0,0 +1,130 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Config
import qs.Components
import qs.Modules
import qs.Helpers
Item {
id: root
required property var wrapper
implicitHeight: profiles.implicitHeight + Appearance.padding.small
implicitWidth: profiles.implicitWidth + Appearance.padding.small * 2
CustomRect {
id: profiles
anchors.horizontalCenter: parent.horizontalCenter
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: updatesList.contentHeight + Appearance.padding.small * 2
implicitWidth: updatesList.contentWidth + Appearance.padding.small * 2
radius: Appearance.rounding.small
CustomListView {
id: updatesList
anchors.centerIn: parent
contentHeight: childrenRect.height
contentWidth: 600
implicitHeight: contentHeight
implicitWidth: contentWidth
spacing: Appearance.spacing.normal
delegate: CustomRect {
id: update
required property var modelData
readonly property list<string> sections: modelData.update.split(" ")
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: 50 + Appearance.padding.smaller * 2
radius: Appearance.rounding.small - Appearance.padding.small
RowLayout {
anchors.fill: parent
anchors.leftMargin: Appearance.padding.smaller
anchors.rightMargin: Appearance.padding.smaller
MaterialIcon {
font.pointSize: Appearance.font.size.large * 2
text: "package_2"
}
ColumnLayout {
Layout.fillWidth: true
CustomText {
Layout.fillWidth: true
Layout.preferredHeight: 25
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
text: update.sections[0]
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
text: Updates.formatUpdateTime(update.modelData.timestamp)
}
}
RowLayout {
Layout.fillHeight: true
Layout.preferredWidth: 300
CustomText {
id: versionFrom
Layout.fillHeight: true
Layout.preferredWidth: 125
color: DynamicColors.palette.m3tertiary
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
text: update.sections[1]
verticalAlignment: Text.AlignVCenter
}
MaterialIcon {
Layout.fillHeight: true
color: DynamicColors.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge
horizontalAlignment: Text.AlignHCenter
text: "arrow_right_alt"
verticalAlignment: Text.AlignVCenter
}
CustomText {
id: versionTo
Layout.fillHeight: true
Layout.preferredWidth: 120
color: DynamicColors.palette.m3primary
elide: Text.ElideRight
font.pointSize: Appearance.font.size.large
horizontalAlignment: Text.AlignHCenter
text: update.sections[3]
verticalAlignment: Text.AlignVCenter
}
}
}
}
model: ScriptModel {
id: script
objectProp: "update"
values: Object.entries(Updates.updates).sort((a, b) => b[1] - a[1]).map(([update, timestamp]) => ({
update,
timestamp
}))
}
}
}
}
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Layouts
import qs.Components
import qs.Modules
import qs.Helpers
import qs.Config
CustomRect {